faastruby 0.4.18 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -3
  3. data/Gemfile.lock +28 -4
  4. data/README.md +63 -5
  5. data/faastruby.gemspec +5 -1
  6. data/lib/faastruby.rb +1 -0
  7. data/lib/faastruby/api.rb +154 -6
  8. data/lib/faastruby/base.rb +3 -9
  9. data/lib/faastruby/cli.rb +39 -12
  10. data/lib/faastruby/cli/base_command.rb +66 -0
  11. data/lib/faastruby/cli/commands.rb +122 -59
  12. data/lib/faastruby/cli/commands/account/base_command.rb +10 -0
  13. data/lib/faastruby/cli/commands/account/confirm.rb +94 -0
  14. data/lib/faastruby/cli/commands/account/login.rb +86 -0
  15. data/lib/faastruby/cli/commands/account/logout.rb +59 -0
  16. data/lib/faastruby/cli/commands/account/signup.rb +76 -0
  17. data/lib/faastruby/cli/commands/{function.rb → function/base_command.rb} +2 -11
  18. data/lib/faastruby/cli/commands/function/build.rb +18 -11
  19. data/lib/faastruby/cli/commands/function/deploy_to.rb +100 -37
  20. data/lib/faastruby/cli/commands/function/new.rb +89 -36
  21. data/lib/faastruby/cli/commands/function/remove_from.rb +21 -6
  22. data/lib/faastruby/cli/commands/function/run.rb +15 -15
  23. data/lib/faastruby/cli/commands/function/test.rb +5 -4
  24. data/lib/faastruby/cli/commands/function/update_context.rb +10 -3
  25. data/lib/faastruby/cli/commands/function/upgrade.rb +62 -61
  26. data/lib/faastruby/cli/commands/help.rb +33 -20
  27. data/lib/faastruby/cli/commands/project/base_command.rb +14 -0
  28. data/lib/faastruby/cli/commands/project/deploy.rb +114 -0
  29. data/lib/faastruby/cli/commands/project/down.rb +58 -0
  30. data/lib/faastruby/cli/commands/project/new.rb +237 -0
  31. data/lib/faastruby/cli/commands/workspace/cp.rb +107 -0
  32. data/lib/faastruby/cli/commands/workspace/create.rb +35 -27
  33. data/lib/faastruby/cli/commands/workspace/destroy.rb +14 -7
  34. data/lib/faastruby/cli/commands/workspace/list.rb +15 -6
  35. data/lib/faastruby/cli/commands/workspace/migrate.rb +93 -0
  36. data/lib/faastruby/cli/commands/workspace/rm.rb +81 -0
  37. data/lib/faastruby/cli/commands/workspace/update.rb +62 -0
  38. data/lib/faastruby/cli/credentials.rb +58 -57
  39. data/lib/faastruby/cli/new_credentials.rb +63 -0
  40. data/lib/faastruby/cli/package.rb +1 -0
  41. data/lib/faastruby/cli/template.rb +7 -7
  42. data/lib/faastruby/local.rb +188 -0
  43. data/lib/faastruby/local/crystal_runtime.cr +170 -0
  44. data/lib/faastruby/local/functions.rb +7 -0
  45. data/lib/faastruby/local/functions/crystal.rb +64 -0
  46. data/lib/faastruby/local/functions/function.rb +173 -0
  47. data/lib/faastruby/local/functions/ruby.rb +28 -0
  48. data/lib/faastruby/local/listeners.rb +5 -0
  49. data/lib/faastruby/local/listeners/listener.rb +104 -0
  50. data/lib/faastruby/local/logger.rb +37 -0
  51. data/lib/faastruby/local/monkey_patch.rb +38 -0
  52. data/lib/faastruby/local/processors.rb +7 -0
  53. data/lib/faastruby/local/processors/function.rb +151 -0
  54. data/lib/faastruby/local/processors/processor.rb +116 -0
  55. data/lib/faastruby/local/processors/static_file.rb +48 -0
  56. data/lib/faastruby/local/static_files.rb +5 -0
  57. data/lib/faastruby/local/static_files/static_file.rb +59 -0
  58. data/lib/faastruby/server.rb +44 -3
  59. data/lib/faastruby/server/app.rb +107 -0
  60. data/lib/faastruby/server/concurrency_controller.rb +50 -50
  61. data/lib/faastruby/server/config.ru +2 -0
  62. data/lib/faastruby/server/event.rb +3 -0
  63. data/lib/faastruby/server/event_hub.rb +7 -6
  64. data/lib/faastruby/server/local.rb +22 -0
  65. data/lib/faastruby/server/logger.rb +50 -0
  66. data/lib/faastruby/server/project_config.rb +44 -0
  67. data/lib/faastruby/server/puma.rb +4 -0
  68. data/lib/faastruby/server/response.rb +40 -0
  69. data/lib/faastruby/server/runner.rb +116 -21
  70. data/lib/faastruby/server/runner_methods.rb +17 -16
  71. data/lib/faastruby/server/sentinel.rb +496 -0
  72. data/lib/faastruby/supported_runtimes.rb +8 -0
  73. data/lib/faastruby/user.rb +77 -0
  74. data/lib/faastruby/version.rb +1 -1
  75. data/lib/faastruby/workspace.rb +36 -3
  76. data/templates/crystal/example-blank/handler.cr +3 -0
  77. data/templates/crystal/example/spec/handler_spec.cr +11 -6
  78. data/templates/public-web/assets/images/background.png +0 -0
  79. data/templates/public-web/assets/images/ruby.png +0 -0
  80. data/templates/public-web/assets/javascripts/main.js +1 -0
  81. data/templates/public-web/assets/stylesheets/main.css +70 -0
  82. data/templates/public-web/favicon.ico +0 -0
  83. data/templates/ruby/api-404/handler.rb +6 -0
  84. data/templates/ruby/api-root/handler.rb +6 -0
  85. data/templates/ruby/example-blank/handler.rb +0 -23
  86. data/templates/ruby/web-404/404.html +36 -0
  87. data/templates/ruby/web-404/handler.rb +3 -0
  88. data/templates/ruby/web-root/handler.rb +10 -0
  89. data/templates/ruby/web-root/index.html.erb +37 -0
  90. data/templates/ruby/web-root/template.rb +13 -0
  91. metadata +102 -21
  92. data/exe/faastruby-server +0 -76
  93. data/lib/faastruby/cli/commands/credentials.rb +0 -11
  94. data/lib/faastruby/cli/commands/credentials/add.rb +0 -58
  95. data/lib/faastruby/cli/commands/credentials/list.rb +0 -58
  96. data/lib/faastruby/cli/commands/workspace.rb +0 -13
  97. data/lib/faastruby/cli/commands/workspace/deploy.rb +0 -50
  98. data/templates/crystal/example-blank/README.md +0 -22
  99. data/templates/crystal/example-blank/spec/handler_spec.cr +0 -8
  100. data/templates/crystal/example-blank/spec/spec_helper.cr +0 -4
  101. data/templates/crystal/example-blank/src/handler.cr +0 -25
  102. data/templates/ruby/example-blank/Gemfile +0 -7
  103. data/templates/ruby/example-blank/README.md +0 -22
  104. data/templates/ruby/example-blank/spec/handler_spec.rb +0 -16
  105. data/templates/ruby/example-blank/spec/spec_helper.rb +0 -3
@@ -0,0 +1,7 @@
1
+ module FaaStRuby
2
+ module Local
3
+ require 'faastruby/local/functions/function'
4
+ require 'faastruby/local/functions/ruby'
5
+ require 'faastruby/local/functions/crystal'
6
+ end
7
+ end
@@ -0,0 +1,64 @@
1
+ module FaaStRuby
2
+ module Local
3
+ class CrystalFunction < Function
4
+ include Local::Logger
5
+ def compile
6
+ debug "compile"
7
+ handler_path = get_handler_path
8
+ debug "File exists? #{handler_path} - #{File.file?(handler_path)}"
9
+ runtime_path = Pathname.new "#{Gem::Specification.find_by_name("faastruby").gem_dir}/lib/faastruby/local/crystal_runtime.cr"
10
+ h_path = Pathname.new(handler_path)
11
+ handler_path = h_path.relative_path_from runtime_path
12
+ build_cmd = "cd #{@absolute_folder} && crystal build #{runtime_path} -o handler"
13
+ debug "Running #{build_cmd}"
14
+ job_id = SecureRandom.uuid
15
+ puts "Job ID=\"#{job_id}\" started: Compiling function '#{@name}'"
16
+ # return false unless precompile
17
+ env = {'HANDLER_PATH' => handler_path.to_s}
18
+ debug "COMPILE ENV: #{env}"
19
+ output, status = Open3.capture2e(env, build_cmd)
20
+ success = status.exitstatus == 0
21
+ if success
22
+ puts "Job ID=\"#{job_id}\" completed: #{status}"
23
+ else
24
+ puts "Job ID=\"#{job_id}\" failed:"
25
+ String.disable_colorization = true
26
+ STDERR.puts output
27
+ STDOUT.puts '---'
28
+ String.disable_colorization = false
29
+ end
30
+ # puts "Job ID=\"#{job_id}\": #{output}" unless success
31
+ # puts "Job ID=\"#{job_id}\" #{success ? 'completed' : 'failed'}: #{status}"
32
+ end
33
+
34
+ def get_handler_path
35
+ if File.file?("#{@absolute_folder}/handler.cr")
36
+ "#{@absolute_folder}/handler"
37
+ else
38
+ "#{@absolute_folder}/src/handler"
39
+ end
40
+ end
41
+
42
+ def yaml_hash
43
+ debug "yaml_hash"
44
+ hash = {
45
+ 'cli_version' => FaaStRuby::VERSION,
46
+ 'name' => @name,
47
+ 'runtime' => DEFAULT_CRYSTAL_RUNTIME
48
+ }
49
+ end
50
+
51
+ def write_handler
52
+ debug "write_handler"
53
+ content = "def handler(event)\n # Write code here\n \nend"
54
+ file = "#{get_handler_path}.cr"
55
+ if File.size(file) > 0
56
+ puts "New Crystal function '#{@name}' detected."
57
+ else
58
+ File.write(file, content)
59
+ puts "New Crystal function '#{@name}' initialized."
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,173 @@
1
+ module FaaStRuby
2
+ module Local
3
+ class MissingConfigurationFileError < StandardError;end
4
+ class Function
5
+ include Local::Logger
6
+ extend Local::Logger
7
+ def self.find_all_in(functions_dir)
8
+ debug "self.find_all_in(#{functions_dir.inspect})"
9
+ Dir.glob(["**/handler.rb", "**/handler.cr"], base: functions_dir).map do |entry|
10
+ function_absolute_folder = "#{functions_dir}/#{File.dirname(entry)}"
11
+ next unless File.file?("#{function_absolute_folder}/faastruby.yml")
12
+ from_yaml(function_absolute_folder)
13
+ end.compact
14
+ end
15
+
16
+ def self.that_has_file(entry, event_type)
17
+ debug "self.that_has_file(#{entry.inspect})"
18
+ absolute_folder = get_function_folder_for(entry)
19
+ if event_type == :removed
20
+ name = absolute_folder.dup
21
+ name.slice!("#{Local.functions_dir}/")
22
+ return new(
23
+ name: name,
24
+ before_build: [],
25
+ absolute_folder: absolute_folder
26
+ )
27
+ end
28
+ from_yaml(absolute_folder)
29
+ end
30
+
31
+ def self.from_yaml(absolute_folder)
32
+ debug "self.from_yaml(#{absolute_folder.inspect})"
33
+ yaml_file = "#{absolute_folder}/faastruby.yml"
34
+ yaml = YAML.load(File.read(yaml_file))
35
+ language, runtime_version = yaml['runtime'].split(':')
36
+ object = Local::RubyFunction if language == 'ruby'
37
+ object = Local::CrystalFunction if language == 'crystal'
38
+ object.new(
39
+ name: yaml['name'],
40
+ before_build: yaml['before_build'],
41
+ absolute_folder: absolute_folder
42
+ )
43
+ end
44
+
45
+ def self.get_function_folder_for(entry)
46
+ return File.dirname(entry) if File.basename(entry) == 'faastruby.yml'
47
+ debug "self.get_function_folder_for(#{entry.inspect})"
48
+ dirname = File.dirname(entry)
49
+ raise MissingConfigurationFileError.new("ERROR: Could not determine which function the file belongs to. Make sure your functions have the configuration file 'faastruby.yml'.") if dirname == SERVER_ROOT
50
+ return dirname if File.file?("#{dirname}/faastruby.yml")
51
+ get_function_folder_for(dirname)
52
+ end
53
+
54
+ #### Instance methods
55
+ attr_accessor :name, :before_build, :absolute_folder
56
+ def initialize(name:, before_build: [], absolute_folder:)
57
+ debug "initialize(name: #{name.inspect}, before_build: #{before_build.inspect}, absolute_folder: #{absolute_folder.inspect})"
58
+ @name = name
59
+ @before_build = before_build || []
60
+ @absolute_folder = absolute_folder
61
+ end
62
+
63
+ def deploy
64
+ debug "deploy"
65
+ deploy_cmd, deploy_cmd_print = generate_deploy_command
66
+ puts "Running: #{deploy_cmd_print.join(' ')}"
67
+ output, status = Open3.capture2e(deploy_cmd.join(' '))
68
+ STDOUT.puts "#{Time.now} | " + "* [#{name}] Deploying...".green
69
+ STDOUT.puts "---"
70
+ String.disable_colorization = true
71
+ if status.exitstatus == 0
72
+ output.split("\n").each {|o| puts o unless o == '---'}
73
+ else
74
+ puts "* [#{name}] Deploy Failed:"
75
+ STDERR.puts output
76
+ end
77
+ String.disable_colorization = false
78
+ end
79
+
80
+ def language
81
+ case YAML.load(File.read("#{@absolute_folder}/faastruby.yml"))['runtime']
82
+ when /^ruby:/
83
+ "ruby"
84
+ when /^crystal:/
85
+ "crystal"
86
+ end
87
+ end
88
+
89
+ def generate_deploy_command
90
+ debug "generate_deploy_command"
91
+ project_config = Local.project_config
92
+ deploy_cmd = ['faastruby', 'deploy-to', Local.workspace, '-f', @absolute_folder, '--dont-create-workspace']
93
+ deploy_cmd << '--set-root' if Local.root_to == @name
94
+ deploy_cmd << '--set-catch-all' if Local.catch_all == @name
95
+ secrets_json = Oj.dump(Local.secrets_for_function(@name)) rescue nil
96
+ deploy_cmd_print = deploy_cmd
97
+ if secrets_json
98
+ deploy_cmd += ["--context", secrets_json]
99
+ deploy_cmd_print += ["--context", '*REDACTED*']
100
+ end
101
+ [deploy_cmd, deploy_cmd_print]
102
+ end
103
+
104
+ def compile
105
+ debug "compile"
106
+ true
107
+ end
108
+
109
+ def remove_from_workspace
110
+ debug "remove_from_workspace"
111
+ remove_cmd = ["faastruby", "remove-from", Local.workspace, "-y", "-f", @name]
112
+ puts "Removing function '#{@name}' from the cloud workspace '#{Local.workspace}'."
113
+ removed = system(*remove_cmd)
114
+ STDOUT.puts '---'
115
+ if removed
116
+ puts "Function '#{@name}' was removed from the cloud workspace '#{Local.workspace}'."
117
+ end
118
+ end
119
+
120
+ def initialize_new_function
121
+ debug "initialize_new_function"
122
+ write_yaml
123
+ write_handler
124
+ end
125
+
126
+ def merge_yaml(hash, yaml_file)
127
+ debug "merge_yaml(#{hash.inspect}, #{yaml_file.inspect})"
128
+ new_config = load_yaml.merge(hash)
129
+ File.write(yaml_file, new_config.to_yaml)
130
+ end
131
+
132
+ def load_yaml
133
+ debug "load_yaml"
134
+ YAML.load(File.read("#{@absolute_folder}/faastruby.yml"))
135
+ end
136
+
137
+ def write_yaml
138
+ debug "write_yaml"
139
+ yaml_file = "#{@absolute_folder}/faastruby.yml"
140
+ if File.file?(yaml_file)
141
+ merge_yaml(yaml_hash, yaml_file)
142
+ else
143
+ File.write(yaml_file, yaml_hash.to_yaml)
144
+ end
145
+ File.open(yaml_file, 'a') do |f|
146
+ f.write yaml_comments
147
+ end
148
+ end
149
+
150
+ def yaml_comments
151
+ [
152
+ '## You can add commands to run locally before building the deployment package.',
153
+ "## Some use cases are:",
154
+ "## * minifying Javascript/CSS",
155
+ "## * downloading a file to be included in the package.",
156
+ "# before_build:",
157
+ "# - curl https://some.url --output some.file",
158
+ "# - uglifyjs your.js -c -m -o your.min.js",
159
+ '',
160
+ '## To schedule periodic runs, follow the example below:',
161
+ '# schedule:',
162
+ '# job1:',
163
+ '# when: every 2 hours',
164
+ '# body: {"foo": "bar"}',
165
+ '# method: POST',
166
+ '# query_params: {"param": "value"}',
167
+ '# headers: {"Content-Type": "application/json"}',
168
+ '# job2: ...'
169
+ ].join("\n")
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,28 @@
1
+ module FaaStRuby
2
+ module Local
3
+ class RubyFunction < Function
4
+ include Local::Logger
5
+
6
+ def yaml_hash
7
+ debug "yaml_hash"
8
+ hash = {
9
+ 'cli_version' => FaaStRuby::VERSION,
10
+ 'name' => @name,
11
+ 'runtime' => DEFAULT_RUBY_RUNTIME
12
+ }
13
+ end
14
+
15
+ def write_handler
16
+ debug "write_handler"
17
+ content = "def handler(event)\n # Write code here\n \nend"
18
+ file = "#{@absolute_folder}/handler.rb"
19
+ if File.size(file) > 0
20
+ puts "New Ruby function '#{@name}' detected."
21
+ else
22
+ File.write(file, content)
23
+ puts "New Ruby function '#{@name}' initialized."
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module FaaStRuby
2
+ module Local
3
+ require 'faastruby/local/listeners/listener'
4
+ end
5
+ end
@@ -0,0 +1,104 @@
1
+ module FaaStRuby
2
+ module Local
3
+ class Listener
4
+ include Local::Logger
5
+ attr_accessor :listener, :directory, :queue
6
+ def initialize(directory:, queue:)
7
+ debug "initialize(directory: #{directory.inspect}, queue: #{queue.inspect})"
8
+ @directory = directory
9
+ @queue = queue
10
+ @listener = ::Listen.to(directory, &callback)
11
+ end
12
+
13
+ def start
14
+ debug "start"
15
+ listener.start
16
+ end
17
+
18
+ def stop
19
+ debug "stop"
20
+ listener.stop
21
+ end
22
+
23
+ def callback
24
+ debug "callback"
25
+ Proc.new do |modified, added, removed|
26
+ begin
27
+ modified.each do |file|
28
+ queue.push ListenerEvent.new(type: :modified, full_path: file, listened_directory: @directory)
29
+ end
30
+ added.each do |file|
31
+ queue.push ListenerEvent.new(type: :added, full_path: file, listened_directory: @directory)
32
+ end
33
+ removed.each do |file|
34
+ queue.push ListenerEvent.new(type: :removed, full_path: file, listened_directory: @directory)
35
+ end
36
+ rescue StandardError => e
37
+ String.disable_colorization = true
38
+ STDOUT.puts e.full_message
39
+ String.disable_colorization = false
40
+ next
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ class ListenerEvent
47
+ include Local::Logger
48
+ attr_accessor :type, :filename, :full_path, :relative_path, :listened_directory, :dirname
49
+ def initialize(type:, full_path:, listened_directory:)
50
+ debug "initialize(type: #{type.inspect}, full_path: #{full_path.inspect}, listened_directory: #{listened_directory.inspect})"
51
+ @listened_directory = listened_directory
52
+ @full_path = full_path
53
+ @relative_path = relative_path_for(@full_path.dup)
54
+ @filename = File.basename(@full_path)
55
+ @dirname = File.dirname(@full_path)
56
+ @type = type
57
+
58
+ debug "EVENT: #{@type}"
59
+ debug "EVENT: #{@full_path}"
60
+ end
61
+
62
+ def function_created?
63
+ debug __method__
64
+ added? && filename.match(/^handler\.(rb|cr)$/)
65
+ end
66
+
67
+ def file_is_a_handler?
68
+ debug __method__
69
+ filename.match(/^handler\.(rb|cr)$/)
70
+ end
71
+
72
+ def file_is_a_function_config?
73
+ debug __method__
74
+ filename == 'faastruby.yml'
75
+ end
76
+
77
+ # def file_was_just_added?
78
+ # debug __method__
79
+ # Time.now.to_i - File.ctime(@full_path).to_i <= 1
80
+ # end
81
+
82
+ def added?
83
+ debug __method__
84
+ @type == :added
85
+ end
86
+
87
+ def modified?
88
+ debug __method__
89
+ @type == :modified
90
+ end
91
+
92
+ def removed?
93
+ debug __method__
94
+ @type == :removed
95
+ end
96
+
97
+ def relative_path_for(full_path)
98
+ debug "relative_path_for(#{full_path.inspect})"
99
+ full_path.slice!("#{@listened_directory}/")
100
+ full_path
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,37 @@
1
+ module FaaStRuby
2
+ module Local
3
+ STDOUT_MUTEX = Mutex.new
4
+ module Logger
5
+ def self.puts(msg)
6
+ STDOUT_MUTEX.synchronize do
7
+ STDOUT.puts "#{Time.now} | #{msg}".yellow
8
+ STDOUT.puts "---".yellow
9
+ end
10
+ end
11
+
12
+ def debug(msg)
13
+ return false unless DEBUG
14
+ name = self.name if ['Module', 'Class'].include? self.class.name
15
+ name ||= self.class.name
16
+ STDOUT_MUTEX.synchronize do
17
+ STDOUT.puts "#{Time.now} | [DEBUG] [#{name}] #{msg}".red
18
+ STDOUT.puts "---".red
19
+ end
20
+ end
21
+
22
+ def puts(msg)
23
+ STDOUT_MUTEX.synchronize do
24
+ STDOUT.puts "#{Time.now} | #{msg}".yellow
25
+ STDOUT.puts "---".yellow
26
+ end
27
+ end
28
+
29
+ def print(msg)
30
+ STDOUT_MUTEX.synchronize do
31
+ STDOUT.print "#{Time.now} | #{msg}".yellow
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ # This is required to address a bug on the Listen gem :(
2
+ # See: https://github.com/guard/listen/issues/426
3
+ module Listen
4
+ class Record
5
+ def dir_entries(rel_path)
6
+ subtree =
7
+ if [nil, '', '.'].include? rel_path.to_s
8
+ tree
9
+ else
10
+ # tree[rel_path.to_s] ||= _auto_hash
11
+ # puts tree[rel_path.to_s]
12
+ # tree[rel_path.to_s]
13
+ _sub_dir_entries(rel_path)
14
+ end
15
+
16
+ result = {}
17
+ subtree.each do |key, values|
18
+ # only get data for file entries
19
+ result[key] = values.key?(:mtime) ? values : {}
20
+ end
21
+ result
22
+ end
23
+
24
+ def _sub_dir_entries(rel_path)
25
+ result = {}
26
+ tree.each do |path, meta|
27
+ next if !path.start_with?(rel_path)
28
+ if path == rel_path
29
+ result.merge!(meta)
30
+ else
31
+ sub_path = path.sub(%r{\A#{rel_path}/?}, '')
32
+ result[sub_path] = meta
33
+ end
34
+ end
35
+ result
36
+ end
37
+ end
38
+ end