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
@@ -1,43 +1,138 @@
1
1
  require 'base64'
2
-
2
+ require 'open3'
3
3
  module FaaStRuby
4
+ # require 'faastruby/server/response'
5
+ # require 'faastruby/server/runner_methods'
6
+ # require 'faastruby/server/function_object'
4
7
  class Runner
5
- include RunnerMethods
6
- def initialize
8
+ def initialize(function_name)
9
+ # puts "initializing runner for function name #{function_name}"
7
10
  @rendered = false
11
+ @function_name = function_name
12
+ @function_folder = "#{FaaStRuby::ProjectConfig.functions_dir}/#{function_name}"
13
+ # puts "function_folder: #{@function_folder}"
14
+ @config_file = "#{@function_folder}/faastruby.yml"
15
+ # puts "reading config file #{@config_file}"
16
+ @config = YAML.load(File.read(@config_file))
17
+ @language, @version = (@config['runtime'] || DEFAULT_RUBY_RUNTIME).split(':')
8
18
  end
9
19
 
10
- def path
11
- @path
12
- end
13
-
14
20
  def load_function(path)
15
- eval "Module.new do; #{File.read(path)};end"
21
+ eval %(
22
+ Module.new do
23
+ def self.require(path)
24
+ return load("\#{path}.rb") if File.file?("\#{path}.rb")
25
+ Kernel.require path
26
+ end
27
+ #{File.read(path)}
28
+ end
29
+ )
16
30
  end
17
31
 
18
- def call(workspace_name, function_name, event, args)
19
- @short_path = "#{workspace_name}/#{function_name}"
20
- @path = "#{FaaStRuby::PROJECT_ROOT}/#{workspace_name}/#{function_name}"
21
- short_path = "#{workspace_name}/#{function_name}"
32
+ def call(event, args)
22
33
  begin
23
- Dir.chdir(@path)
24
- function = load_function("#{@path}/handler.rb")
25
- runner = FunctionObject.new(short_path)
26
- runner.extend(function)
27
- response = runner.handler(event, *args)
28
- # response = handler(event, args)
29
- return response if response.is_a?(FaaStRuby::Response)
34
+ case @language
35
+ when 'ruby'
36
+ time, response = call_ruby(event, args)
37
+ when 'crystal'
38
+ time, response = call_crystal(event, args)
39
+ else
40
+ puts "[Runner] ERROR: could not determine the language for function #{@function_name}.".red
41
+ end
42
+ return [time, response] if response.is_a?(FaaStRuby::Response)
30
43
  body = {
31
44
  'error' => "Please use the helpers 'render' or 'respond_with' as your function return value."
32
45
  }
33
- FaaStRuby::Response.new(body: Oj.dump(body), status: 500, headers: {'Content-Type' => 'application/json'})
46
+ [time, FaaStRuby::Response.new(body: Oj.dump(body), status: 500, headers: {'Content-Type' => 'application/json'})]
34
47
  rescue Exception => e
48
+ STDOUT.puts e.full_message
35
49
  body = {
36
50
  'error' => e.message,
37
51
  'location' => e.backtrace&.first,
38
52
  }
39
- FaaStRuby::Response.new(body: Oj.dump(body), status: 500, headers: {'Content-Type' => 'application/json'})
53
+ [0.0, FaaStRuby::Response.new(body: Oj.dump(body), status: 500, headers: {'Content-Type' => 'application/json'})]
54
+ end
55
+ end
56
+ def call_ruby(event, args)
57
+ function_object = FunctionObject.new(@function_name)
58
+ reader, writer = IO.pipe
59
+ time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
60
+ pid = fork do
61
+ # puts "loading #{@function_folder}/handler.rb"
62
+ Dir.chdir(@function_folder)
63
+ begin
64
+ function = load_function("#{@function_folder}/handler.rb")
65
+ function_object.extend(function)
66
+ response = function_object.handler(event, *args)
67
+ raise FaaStRuby::Response::InvalidResponseError unless response.is_a?(FaaStRuby::Response)
68
+ rescue Exception => e
69
+ error = Oj.dump({
70
+ 'error' => e.message,
71
+ 'location' => e.backtrace&.first
72
+ })
73
+ response = FaaStRuby::Response.error(error)
74
+ end
75
+ writer.puts response.payload
76
+ writer.close
77
+ exit 0
78
+ end
79
+ response = FaaStRuby::Response.from_payload reader.gets.chomp
80
+ reader.close
81
+ Process.wait(pid)
82
+ time_finish = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
83
+ time = (time_finish - time_start).round(2)
84
+ [time, response]
85
+ end
86
+
87
+ def chdir
88
+ CHDIR_MUTEX.synchronize do
89
+ # puts "Switching to directory #{@function_folder}"
90
+ Dir.chdir(@function_folder)
91
+ yield
92
+ end
93
+ end
94
+
95
+ def call_crystal(event, args)
96
+ ####
97
+ # This is a hack to address the bug https://github.com/crystal-lang/crystal/issues/7052
98
+ event.query_params.each do |k, v|
99
+ event.query_params[k] = '' if v.nil?
100
+ end
101
+ event.headers.each do |k, v|
102
+ event.headers[k] = '' if v.nil?
103
+ end
104
+ ####
105
+ payload_json = Oj.dump({'event' => event.to_h, 'args' => []})
106
+ payload = Base64.urlsafe_encode64(payload_json, padding: false)
107
+ cmd = "./handler"
108
+ # STDOUT.puts "Running #{cmd}"
109
+ # STDOUT.puts "From #{@function_folder}"
110
+ output = nil
111
+ time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
112
+ Open3.popen2(cmd, chdir: @function_folder) do |stdin, stdout, status|
113
+ stdin.puts payload
114
+ stdout.each_line do |line|
115
+ if line[0..1] == 'R,'
116
+ output = line.chomp
117
+ break
118
+ else
119
+ puts line.chomp
120
+ end
121
+ end
40
122
  end
123
+ time_finish = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
124
+ time = (time_finish - time_start).round(2)
125
+ tag, o = output.split(',')
126
+ decoded_response = Base64.urlsafe_decode64(o)
127
+ response_obj = Oj.load(decoded_response)
128
+ # STDOUT.puts response_obj
129
+ response = FaaStRuby::Response.new(
130
+ body: response_obj['response'],
131
+ status: response_obj['status'],
132
+ headers: response_obj['headers'],
133
+ binary: response_obj['binary']
134
+ )
135
+ [time, response]
41
136
  end
42
137
  end
43
138
  end
@@ -1,5 +1,6 @@
1
1
  module FaaStRuby
2
2
  module RunnerMethods
3
+ require 'yaml'
3
4
  def rendered!
4
5
  @rendered = true
5
6
  end
@@ -52,7 +53,7 @@ module FaaStRuby
52
53
  resp_body = bin ? Base64.urlsafe_encode64(body) : body
53
54
  when data
54
55
  headers["Content-Type"] ||= "application/octet-stream"
55
- resp_body = Base64.urlsafe_encode64(data)
56
+ resp_body = Base64.urlsafe_encode64(data, padding: false)
56
57
  bin = true
57
58
  when js
58
59
  headers["Content-Type"] ||= "text/javascript"
@@ -62,22 +63,22 @@ module FaaStRuby
62
63
  resp_body = css
63
64
  when png
64
65
  headers["Content-Type"] ||= "image/png"
65
- resp_body = Base64.urlsafe_encode64(png)
66
+ resp_body = Base64.urlsafe_encode64(File.binread(png), padding: false)
66
67
  bin = true
67
68
  when svg
68
69
  headers["Content-Type"] ||= "image/svg+xml"
69
70
  resp_body = svg
70
71
  when jpeg
71
72
  headers["Content-Type"] ||= "image/jpeg"
72
- resp_body = Base64.urlsafe_encode64(jpeg)
73
+ resp_body = Base64.urlsafe_encode64(File.binread(jpeg), padding: false)
73
74
  bin = true
74
75
  when gif
75
76
  headers["Content-Type"] ||= "image/gif"
76
- resp_body = Base64.urlsafe_encode64(gif)
77
+ resp_body = Base64.urlsafe_encode64(File.binread(gif), padding: false)
77
78
  bin = true
78
79
  when icon
79
80
  headers["Content-Type"] ||= "image/x-icon"
80
- resp_body = Base64.urlsafe_encode64(icon)
81
+ resp_body = Base64.urlsafe_encode64(File.binread(icon), padding: false)
81
82
  bin = true
82
83
  end
83
84
  respond_with(resp_body, status: status, headers: headers, binary: bin)
@@ -89,18 +90,18 @@ module FaaStRuby
89
90
  end
90
91
 
91
92
  def puts(msg)
92
- super "[#{@short_path}] #{msg}".green
93
+ super "[#{@path}] #{msg}".green
93
94
  end
94
95
 
95
- def publish(channel, data: nil)
96
- begin
97
- encoded_data = data ? Base64.urlsafe_encode64(data, padding: false) : ""
98
- payload = %(#{Base64.urlsafe_encode64(channel, padding: false)},#{encoded_data})
99
- EventHub.queue.push payload
100
- true
101
- rescue
102
- false
103
- end
104
- end
96
+ # def publish(channel, data: nil)
97
+ # begin
98
+ # encoded_data = data ? Base64.urlsafe_encode64(data, padding: false) : ""
99
+ # payload = %(#{Base64.urlsafe_encode64(channel, padding: false)},#{encoded_data})
100
+ # EventHub.queue.push payload
101
+ # true
102
+ # rescue
103
+ # false
104
+ # end
105
+ # end
105
106
  end
106
107
  end
@@ -0,0 +1,496 @@
1
+ # # Here 'function_folder' is the function folder.
2
+ # require 'open3'
3
+ # require 'tempfile'
4
+ # require 'pathname'
5
+ # require 'yaml'
6
+ # require 'listen'
7
+ # require 'colorize'
8
+ # require 'securerandom'
9
+
10
+ # module FaaStRuby
11
+ # module Sentinel
12
+ # extend FaaStRuby::Logger::System
13
+ # STATIC_FILES_SYNC_ENABLED = SYNC_ENABLED && FaaStRuby::ProjectConfig.public_dir?
14
+ # STOPPER_QUEUE = Queue.new
15
+ # @@threads = {}
16
+ # MUTEX = Mutex.new
17
+ # def self.quit
18
+ # Process.kill("HUP", FaaStRuby.sentinel_pid)
19
+ # end
20
+
21
+ # def self.add_thread(function_folder, key, value)
22
+ # MUTEX.synchronize do
23
+ # @@threads[function_folder] ||= {}
24
+ # @@threads[function_folder][key] = value
25
+ # end
26
+ # end
27
+ # def self.get_thread(function_folder, key)
28
+ # MUTEX.synchronize do
29
+ # return nil if @@threads[function_folder].nil?
30
+ # @@threads[function_folder][key]
31
+ # end
32
+ # end
33
+
34
+ # def self.get_threads
35
+ # MUTEX.synchronize do
36
+ # @@threads
37
+ # end
38
+ # end
39
+
40
+ # def self.tag
41
+ # '(Sentinel)'
42
+ # end
43
+
44
+ # def self.try_workspace
45
+ # puts "#{tag} Connecting to workspace '#{WORKSPACE_NAME}'..."
46
+ # try_to_create = Proc.new {system("faastruby create-workspace #{WORKSPACE_NAME}")}
47
+ # has_credentials = system("faastruby list-workspace #{WORKSPACE_NAME} > /dev/null 2>&1")
48
+ # continue = has_credentials || try_to_create.call
49
+ # unless continue
50
+ # puts "[FATAL] Unable to setup project workspace '#{WORKSPACE_NAME}'. Make sure you have the credentials, or try a different environment name.\nExample: faastruby local --sync --deploy-env #{DEPLOY_ENVIRONMENT}-#{(rand * 100).to_i}".red
51
+ # exit 1
52
+ # end
53
+ # true
54
+ # end
55
+
56
+ # def self.watch_for_live_compile(functions)
57
+ # functions.each do |path|
58
+ # # puts "Starting live compile for #{path}"
59
+ # watch_function_for_live_compile(path)
60
+ # end
61
+ # end
62
+
63
+ # def self.watch_function_for_live_compile(path)
64
+ # function_folder = File.expand_path path
65
+ # # FileUtils.rm_f("#{function_folder}/handler")
66
+ # # FileUtils.rm_f("#{function_folder}/handler.dwarf")
67
+ # add_thread(function_folder, 'watcher', start_watcher_for(function_folder))
68
+ # # This will force compile when the server starts
69
+ # trigger("#{function_folder}/faastruby.yml")
70
+ # end
71
+
72
+ # def self.start_watcher_for(function_folder)
73
+ # function_name = get_function_name(function_folder)
74
+ # puts "#{tag} Live compiling enabled for '#{function_name}'."
75
+ # exclude_from_watcher = [
76
+ # "#{function_folder}/handler",
77
+ # "#{function_folder}/handler.dwarf",
78
+ # "#{function_folder}/.package.zip"
79
+ # ]
80
+ # new_watcher_thread(function_folder, exclude_from_watcher, function_name)
81
+ # end
82
+
83
+ # def self.stop_watcher(listener, function_name)
84
+ # STOPPER_QUEUE << [listener, function_name]
85
+ # end
86
+
87
+ # def self.new_watcher_thread(function_folder, exclude_from_watcher, function_name)
88
+ # handler_path = get_handler_path_in(function_folder)
89
+ # listener = Listen.to(function_folder) do |modified, added, removed|
90
+ # full_path, relative_path, event = translate(modified, added, removed)
91
+ # next if exclude_from_watcher.include?(full_path)
92
+ # file_name = File.basename(full_path)
93
+ # puts "#{tag} Previous Job for '#{function_name}' aborted" if kill_thread_if_alive(function_folder, 'running', function_name)
94
+ # # puts "#{tag} Event: #{full_path}, #{event}"
95
+ # if event == :removed && is_a_function?(full_path, function_folder)
96
+ # puts "#{tag} Disabling watcher for function '#{function_name}'."
97
+ # stop_watcher(get_thread(function_folder, 'watcher'), function_name)
98
+ # # listener = get_thread(function_folder, 'watcher')
99
+ # # listener.stop
100
+ # # # Thread.current.exit
101
+ # next
102
+ # end
103
+ # add_thread(function_folder, 'running', Thread.new {CrystalBuild.new(function_folder, handler_path, run_before_build: true).start})
104
+ # end
105
+ # listener.start
106
+ # listener
107
+ # end
108
+
109
+ # def self.detect_new_functions(target, language)
110
+ # puts "#{tag} Watching for new #{language} functions..."
111
+ # only = /\/handler.rb$/ if language == 'Ruby'
112
+ # only = /\/handler.cr$/ if language == 'Crystal'
113
+ # listener = Listen.to(target, only: only) do |modified, added, removed|
114
+ # full_path, relative_path, event = translate(modified, added, removed)
115
+ # # Filewatcher.new(target).watch do |full_path, event|
116
+ # next unless event == :added
117
+ # file_name = File.basename(full_path)
118
+ # function_folder = File.dirname(full_path)
119
+ # function_name = get_function_name(function_folder)
120
+ # yield(function_folder, file_name, full_path, function_name)
121
+ # enable_sync_for(function_folder, delay: 1) if SYNC_ENABLED
122
+ # end
123
+ # listener.start
124
+ # listener
125
+ # end
126
+
127
+ # def self.start_crystal(functions)
128
+ # puts "#{tag} Crystal functions: #{functions}"
129
+ # enable_sync(functions, delay: 1) if SYNC_ENABLED
130
+ # watch_for_live_compile(functions) unless SYNC_ENABLED
131
+ # detect_new_functions(Dir.pwd, 'Crystal') do |function_folder, file, full_path, function_name|
132
+ # function_folder = normalize_crystal_folder(function_folder)
133
+ # add_configuration(function_folder, file, full_path)
134
+ # puts "#{tag} New Crystal function detected at '#{function_name}'."
135
+ # write_handler(full_path, 'crystal') unless File.size(full_path) > 0
136
+ # add_thread(function_folder, 'watcher', start_watcher_for(function_folder))
137
+ # # trigger(full_path)
138
+ # end
139
+ # end
140
+
141
+ # def self.start_ruby(functions)
142
+ # puts "#{tag} Ruby functions: #{functions}"
143
+ # enable_sync(functions) if SYNC_ENABLED
144
+ # listener = detect_new_functions(Dir.pwd, 'Ruby') do |function_folder, file, full_path, function_name|
145
+ # add_configuration(function_folder, file, full_path)
146
+ # puts "#{tag} New Ruby function detected at '#{function_name}'."
147
+ # write_handler(full_path, 'ruby') unless File.size(full_path) > 0
148
+ # end
149
+ # end
150
+
151
+ # def self.start_public
152
+ # puts "#{tag} Watching public folder '#{File.basename(FaaStRuby::ProjectConfig.public_dir)}'..."
153
+ # add_thread(FaaStRuby::ProjectConfig.public_dir, 'sync', start_public_sync)
154
+ # end
155
+
156
+ # ########################
157
+
158
+ # def self.start_public_sync(delay: nil)
159
+ # sleep delay if delay
160
+ # public_dir = FaaStRuby::ProjectConfig.public_dir
161
+ # listener = Listen.to(public_dir) do |modified, added, removed|
162
+ # full_path, relative_path, event = translate(modified, added, removed)
163
+ # # puts "FULL_PATH: #{full_path}"
164
+ # # puts "RELATIVE_PATH: #{relative_path}"
165
+ # # puts "EVENT: #{event}"
166
+ # # next if File.directory?(full_path)
167
+ # file_name = File.basename(full_path)
168
+ # puts "#{tag} Previous upload of '#{FaaStRuby::ProjectConfig.public_dir(absolute: false)}/#{relative_path}' aborted" if kill_thread_if_alive(full_path, 'deploying', full_path)
169
+
170
+ # if event == :removed
171
+ # cmd = "faastruby rm #{WORKSPACE_NAME}:/#{relative_path}"
172
+ # puts "#{tag} Running: #{cmd}"
173
+ # system(cmd)
174
+ # add_thread(full_path, 'deployed', nil)
175
+ # next
176
+ # end
177
+ # # deploy_cmd, deploy_cmd_print = faastruby_cp(filename)
178
+ # cmd = "faastruby cp #{full_path} #{WORKSPACE_NAME}:/#{relative_path}"
179
+ # puts "#{tag} Running: #{cmd}"
180
+ # add_thread(full_path, 'deploying', Thread.new {system("cd #{SERVER_ROOT} && #{cmd}")})
181
+ # add_thread(full_path, 'deployed', true)
182
+ # end
183
+ # listener.start
184
+ # listener
185
+ # end
186
+
187
+ # def self.translate(modified, added, removed)
188
+ # return [modified[0], relative_path_for(modified[0].dup), :modified] if modified.any?
189
+ # return [added[0], relative_path_for(added[0].dup), :added] if added.any?
190
+ # return [removed[0], relative_path_for(removed[0].dup), :removed] if removed.any?
191
+ # end
192
+
193
+ # # def self.final_path_and_subject(full_path)
194
+ # # relative_path = relative_path_for(full_path)
195
+ # # prefix = relative_path.slice!(/^(public|functions)\//)
196
+ # # subject = :static if File.expand_path(prefix) == "#{FaaStRuby::ProjectConfig.public_dir}"
197
+ # # subject = :function if File.expand_path(prefix) == "#{FaaStRuby::ProjectConfig.functions_dir}"
198
+ # # [relative_path_for(full_path), subject]
199
+ # # end
200
+
201
+ # def self.relative_path_for(full_path)
202
+ # full_path.slice!("#{SERVER_ROOT}/#{FaaStRuby::ProjectConfig.public_dir(absolute: false)}/")
203
+ # full_path
204
+ # end
205
+
206
+ # ####################
207
+
208
+ # def self.enable_sync(functions, delay: nil)
209
+ # functions.each do |path|
210
+ # function_folder = File.expand_path path
211
+ # enable_sync_for(function_folder, delay: delay)
212
+ # end
213
+ # end
214
+
215
+ # def self.enable_sync_for(function_folder, delay: nil)
216
+ # add_thread(function_folder, 'sync', start_sync_for(function_folder, delay: delay))
217
+ # end
218
+
219
+ # def self.start_listener_monitor
220
+ # Thread.new do
221
+ # loop do
222
+ # listener, function_name = STOPPER_QUEUE.pop
223
+ # listener.stop
224
+ # puts "#{tag} Watcher for function '#{function_name}' was stopped."
225
+ # end
226
+ # end
227
+ # end
228
+
229
+ # def self.start!
230
+ # Dir.chdir FaaStRuby::ProjectConfig.functions_dir
231
+ # start_listener_monitor
232
+ # functions = find_functions
233
+ # listeners = {}
234
+ # puts "#{tag} Sync mode enabled. Your functions will be auto-deployed to the workspace '#{WORKSPACE_NAME}'." if SYNC_ENABLED
235
+ # listeners['ruby'] = start_ruby(functions['ruby']) if RUBY_ENABLED
236
+ # listeners['crystal'] = start_crystal(functions['crystal']) if CRYSTAL_ENABLED
237
+ # # aaa # gotta finish configure public sync
238
+ # listeners['public'] = start_public if STATIC_FILES_SYNC_ENABLED
239
+ # function_listeners = listeners['crystal'].merge(listeners['ruby'])
240
+ # function_listeners.each do
241
+ # sleep
242
+ # ensure
243
+ # listeners.each {|language, listener| listener.stop}
244
+ # end
245
+
246
+ # def self.normalize_crystal_folder(function_folder)
247
+ # if function_folder.match(/src$/) && File.file?("#{function_folder}/../faastruby.yml") && File.file?("#{function_folder}/handler.cr")
248
+ # function_folder.sub!(/\/src$/, '')
249
+ # end
250
+ # function_folder
251
+ # end
252
+
253
+ # def self.write_handler(filename, runtime)
254
+ # content = "def handler(event)\n # Write code here\n \nend" if runtime == 'ruby'
255
+ # content = "def handler(event : FaaStRuby::Event) : FaaStRuby::Response\n # Write code here\n \nend" if runtime == 'crystal'
256
+ # File.write(filename, content)
257
+ # end
258
+
259
+ # def self.get_function_name(function_folder)
260
+ # # f_dir = FaaStRuby::ProjectConfig.functions_dir.dup
261
+ # # f_dir.slice!(function_folder)
262
+ # # f_dir
263
+ # (function_folder.split('/') - FaaStRuby::ProjectConfig.functions_dir.split('/')).join('/')
264
+ # end
265
+
266
+ # def self.add_configuration(function_folder, file, filename)
267
+ # if File.file?("#{function_folder}/faastruby.yml")
268
+ # merge_yaml(function_folder, runtime: default_runtime(file))
269
+ # else
270
+ # write_yaml(function_folder, runtime: default_runtime(file))
271
+ # end
272
+ # end
273
+
274
+ # def self.default_runtime(handler)
275
+ # case handler
276
+ # when 'handler.rb'
277
+ # return DEFAULT_RUBY_RUNTIME
278
+ # when 'handler.cr'
279
+ # return DEFAULT_CRYSTAL_RUNTIME
280
+ # end
281
+ # end
282
+
283
+ # def self.trigger(file)
284
+ # Thread.new do
285
+ # sleep 0.5
286
+ # FileUtils.touch(file)
287
+ # Thread.exit
288
+ # end
289
+ # end
290
+
291
+ # def self.merge_yaml(function_folder, runtime:)
292
+ # yaml = YAML.load(File.read("#{function_folder}/faastruby.yml"))
293
+ # write_yaml(function_folder, runtime: runtime, original: yaml)
294
+ # end
295
+
296
+ # def self.write_yaml(function_folder, runtime:, original: nil)
297
+ # function_name = get_function_name(function_folder)
298
+ # hash = {
299
+ # 'cli_version' => FaaStRuby::VERSION,
300
+ # 'name' => function_name,
301
+ # 'runtime' => runtime
302
+ # }
303
+ # hash = original.merge(hash) if original
304
+ # File.write("#{function_folder}/faastruby.yml", hash.to_yaml)
305
+ # # unless original
306
+ # File.open("#{function_folder}/faastruby.yml", 'a') do |f|
307
+ # f.write yaml_comments
308
+ # end
309
+ # # end
310
+ # puts "#{tag} File created: '#{function_name}/faastruby.yml'"
311
+ # end
312
+
313
+ # def self.yaml_comments
314
+ # [
315
+ # '## You can add commands to run locally before building the deployment package.',
316
+ # "## Some use cases are:",
317
+ # "## * minifying Javascript/CSS",
318
+ # "## * downloading a file to be included in the package.",
319
+ # "# before_build:",
320
+ # "# - curl https://some.url --output some.file",
321
+ # "# - uglifyjs your.js -c -m -o your.min.js",
322
+ # '',
323
+ # '## To schedule periodic runs, follow the example below:',
324
+ # '# schedule:',
325
+ # '# job1:',
326
+ # '# when: every 2 hours',
327
+ # '# body: {"foo": "bar"}',
328
+ # '# method: POST',
329
+ # '# query_params: {"param": "value"}',
330
+ # '# headers: {"Content-Type": "application/json"}',
331
+ # '# job2: ...'
332
+ # ].join("\n")
333
+ # end
334
+
335
+ # def self.start_sync_for(function_folder, delay: nil)
336
+ # # function_name = get_function_name(function_folder)
337
+ # # puts "#{tag} Sync activated for function '#{function_name}'."
338
+ # Thread.new do
339
+ # start_sync(function_folder, delay: delay)
340
+ # end
341
+ # end
342
+
343
+ # def self.remove_from_cloud(function_name, function_folder)
344
+ # remove_cmd = ["faastruby", "remove-from", WORKSPACE_NAME, "-y", "-f", function_name]
345
+ # removed = system(*remove_cmd)
346
+ # add_thread(function_folder, 'deployed', nil)
347
+ # if removed
348
+ # puts "#{tag} Function '#{function_name}' was removed from the cloud."
349
+ # else
350
+ # puts "#{tag} The workspace '#{WORKSPACE_NAME}' had no function named '#{function_name}', please ignore the message in red."
351
+ # end
352
+ # end
353
+
354
+ # def self.kill_thread_if_alive(function_folder, kind, function_name)
355
+ # thr = get_thread(function_folder, kind)
356
+ # if thr&.alive?
357
+ # Thread.kill(thr)
358
+ # return true
359
+ # end
360
+ # return false
361
+ # end
362
+
363
+ # def self.start_sync(function_folder, delay: nil)
364
+ # sleep delay if delay
365
+ # function_name = get_function_name(function_folder)
366
+ # exclude_from_watcher = [
367
+ # "#{function_folder}/handler",
368
+ # "#{function_folder}/handler.dwarf",
369
+ # "#{function_folder}/.package.zip"
370
+ # ]
371
+ # listener = Listen.to(function_folder) do |modified, added, removed|
372
+ # full_path, relative_path, event = translate(modified, added, removed)
373
+ # next if exclude_from_watcher.include?(full_path)
374
+ # puts "#{tag} Previous Deploy for '#{function_name}' aborted" if kill_thread_if_alive(function_folder, 'deploying', function_name)
375
+ # if event == :removed && is_a_function?(full_path, function_folder)
376
+ # remove_from_project(function_folder, function_name)
377
+ # next
378
+ # end
379
+ # deploy_cmd, deploy_cmd_print = generate_deploy_command(function_name, function_folder)
380
+ # puts "#{tag} Running: #{deploy_cmd_print.join(' ')}"
381
+ # deploy(function_folder, deploy_cmd)
382
+ # end
383
+ # listener.start
384
+ # listener
385
+ # end
386
+
387
+ # def self.is_a_function?(filename, function_folder)
388
+ # filename == function_folder || filename.match(/#{function_folder}\/(handler\.(rb|cr)|faastruby.yml)/)
389
+ # end
390
+
391
+ # def self.remove_from_project(function_folder, function_name)
392
+ # Thread.kill(get_thread(function_folder, 'sync'))
393
+ # add_thread(function_folder, 'sync', nil)
394
+ # puts "#{tag} Function '#{function_name}' was removed from the project."
395
+ # remove_from_cloud(function_name, function_folder)# if get_thread(function_folder, 'deployed')
396
+ # end
397
+
398
+ # def self.deploy(function_folder, deploy_cmd)
399
+ # add_thread(function_folder, 'deploying', Thread.new {system(*deploy_cmd)})
400
+ # add_thread(function_folder, 'deployed', true)
401
+ # end
402
+
403
+ # def self.generate_deploy_command(function_name, function_folder)
404
+ # project_config = FaaStRuby::ProjectConfig.project_config
405
+ # deploy_cmd = ['faastruby', 'deploy-to', WORKSPACE_NAME, '-f', function_name]
406
+ # deploy_cmd << '--set-root' if FaaStRuby::ProjectConfig.root_to == function_name
407
+ # deploy_cmd << '--set-catch-all' if FaaStRuby::ProjectConfig.catch_all == function_name
408
+ # secrets_json = Oj.dump(FaaStRuby::ProjectConfig.secrets_for_function(function_name)) rescue nil
409
+ # deploy_cmd_print = deploy_cmd
410
+ # if secrets_json
411
+ # deploy_cmd += ["--context", secrets_json]
412
+ # deploy_cmd_print += ["--context", '*REDACTED*']
413
+ # end
414
+ # [deploy_cmd, deploy_cmd_print]
415
+ # end
416
+
417
+ # def self.get_handler_path_in(function_folder)
418
+ # if File.file?("#{function_folder}/handler.cr")
419
+ # "#{function_folder}/handler"
420
+ # else
421
+ # "#{function_folder}/src/handler"
422
+ # end
423
+ # end
424
+
425
+ # def self.check_for_yaml_file(function_folder, handler_file)
426
+ # yaml_file = "#{function_folder}/faastruby.yml"
427
+ # unless File.file?(yaml_file)
428
+ # puts "#{tag} Function '#{function_folder}' did not have a YML configuration file."
429
+ # write_yaml(function_folder, runtime: default_runtime(File.basename(handler_file)), original: nil)
430
+ # end
431
+ # YAML.load(File.read yaml_file)
432
+ # end
433
+
434
+ # def self.find_functions
435
+ # crystal_functions = []
436
+ # ruby_functions = []
437
+ # Dir.glob(["**/handler.rb", "**/handler.cr"]).each do |handler_file|
438
+ # function_folder = File.dirname(handler_file)
439
+ # function_folder.sub!(/\/src$/, '') if handler_file.match(/src\/handler\.cr$/)
440
+ # yaml = check_for_yaml_file(function_folder, handler_file)
441
+ # case yaml['runtime']
442
+ # when /^crystal:/
443
+ # crystal_functions << function_folder
444
+ # when /^ruby:/
445
+ # ruby_functions << function_folder
446
+ # end
447
+ # end
448
+ # {'crystal' => crystal_functions, 'ruby' => ruby_functions}
449
+ # end
450
+ # end
451
+
452
+ # class CrystalBuild
453
+ # include FaaStRuby::Logger::System
454
+ # def initialize(directory, handler_path, run_before_build: false)
455
+ # @directory = directory
456
+ # @function_name = Sentinel.get_function_name(directory)
457
+ # @runtime_path = Pathname.new "#{Gem::Specification.find_by_name("faastruby").gem_dir}/lib/faastruby/server/crystal_runtime.cr"
458
+ # h_path = Pathname.new(handler_path)
459
+ # @handler_path = h_path.relative_path_from @runtime_path
460
+ # @env = {'HANDLER_PATH' => @handler_path.to_s}
461
+ # @run_before_build = run_before_build
462
+ # @crystal_build = "cd #{@directory} && crystal build #{@runtime_path} -o handler"
463
+ # end
464
+
465
+ # def pre_compile_list
466
+ # return [] unless @run_before_build
467
+ # YAML.load(File.read("#{@directory}/faastruby.yml"))["before_build"] || []
468
+ # end
469
+
470
+ # def precompile
471
+ # pre_compile_list.each do |cmd|
472
+ # cmd = "cd #{@directory} && #{cmd}"
473
+ # puts "#{tag} Job ID=\"#{job_id}\" running before_build: '#{cmd}'"
474
+ # output, status = Open3.capture2e(cmd)
475
+ # success = status.exitstatus == 0
476
+ # unless success
477
+ # puts "#{tag} Job ID=\"#{job_id}\": #{output}"
478
+ # puts "#{tag} Job ID=\"#{job_id}\" failed: #{status}"
479
+ # return false
480
+ # end
481
+ # end
482
+ # return true
483
+ # end
484
+
485
+ # def start
486
+ # Thread.report_on_exception = false
487
+ # job_id = SecureRandom.uuid
488
+ # puts "#{tag} Job ID=\"#{job_id}\" started: Compiling function '#{@function_name}'"
489
+ # return false unless precompile
490
+ # output, status = Open3.capture2e(@env, @crystal_build)
491
+ # success = status.exitstatus == 0
492
+ # puts "#{tag} Job ID=\"#{job_id}\": #{output}" unless success
493
+ # puts "#{tag} Job ID=\"#{job_id}\" #{success ? 'completed' : 'failed'}: #{status}"
494
+ # end
495
+ # end
496
+ # end