factor 0.1.10 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@ module Factor
5
5
  class Listener
6
6
  attr_accessor :workflow_id, :event
7
7
 
8
- def start(params,&code)
8
+ def start(user_id,channel_name,listener_name,event,workflow_name,params,&code)
9
9
  end
10
10
 
11
11
  def stop()
@@ -1,36 +1,34 @@
1
1
  require 'rubygems'
2
2
  require 'cli/command'
3
- require 'zip/zip'
4
- require 'zip/zipfilesystem'
5
3
 
6
4
  module Factor
7
5
  module CLI
8
6
  class ChannelTask < Command
9
-
10
- desc "call CHANNEL METHOD TARGET","start a workflow"
11
- method_option :parameters, :type=>:hash, :default=>{}, :required=>false
12
- def call(channel, method, target)
13
-
14
- puts "not implemented"
15
-
16
- end
17
-
18
-
19
-
20
7
  desc "list", "list all the channels"
21
8
  #method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
22
9
  def list
23
- puts @client.get_channels
10
+ @client.get_channels().each do |channel|
11
+ puts "#{channel['name']} (#{channel['slug']})"
12
+ puts " Listeners:" if channel['listeners'].count>0
13
+ channel['listeners'].each do |listener|
14
+ puts " #{listener['name']}: #{listener['description']}"
15
+ end
16
+ puts " Actions:" if channel['actions'].count>0
17
+ channel['actions'].each do |action|
18
+ puts " #{action['name']}: #{action['description']}"
19
+ end
20
+ end
24
21
  end
25
22
 
26
- desc "add DIRECTORY DEFINITION", "add a key and value for the credential"
27
- def add(directory,definition_file)
28
- puts @client.add_channel(directory,definition_file)
23
+ desc "create DIRECTORY DEFINITION", "add a key and value for the credential"
24
+ method_option :organization, :alias=>"-o", :type=>:string, :desc=>"Organizoation to which this workflow belongs"
25
+ def create(directory,definition_file)
26
+ puts @client.create_channel(directory,definition_file,options[:organization])["notice"]
29
27
  end
30
28
 
31
- desc "remove NAME", "remove a workflow"
32
- def remove(name)
33
- puts @client.remove_channel(name)
29
+ desc "delete NAME", "remove a workflow"
30
+ def delete(name)
31
+ puts @client.delete_channel(name)["notice"]
34
32
  end
35
33
 
36
34
 
data/lib/cli/command.rb CHANGED
@@ -16,7 +16,7 @@ module Factor
16
16
  def initialize(*vals)
17
17
  @config_file_dir = File.expand_path("~/.factor")
18
18
  @client = Factor::Client::Client.new
19
- @client.login_token(get_config[:token])
19
+ @client.login_token(get_config['token'])
20
20
  super(*vals)
21
21
  end
22
22
 
@@ -24,7 +24,7 @@ module Factor
24
24
  File.open(@config_file_dir,'w') do |file|
25
25
  YAML::dump(config,file)
26
26
  end
27
- @client.login_token(get_config[:token])
27
+ @client.login_token(get_config['token'])
28
28
  end
29
29
 
30
30
  def get_config
@@ -33,4 +33,4 @@ module Factor
33
33
  end
34
34
  end
35
35
  end
36
- end
36
+ end
@@ -6,24 +6,32 @@ module Factor
6
6
  class CredentialTask < Command
7
7
 
8
8
 
9
- desc "set SERVICE NAME VALUE", "add a key and value for the credential"
9
+ desc "create SERVICE NAME VALUE", "add a key and value for the credential"
10
10
  method_option :key, :type=>:string, :desc=>"File reference containing the symmetric key for encryption"
11
- def set(service,name,value)
12
- securet=nil
11
+ method_option :organization, :type=>:string, :desc=>"Organizoation to which this credential belongs"
12
+ def create(service,name,value)
13
+ secret=nil
13
14
  if options[:key]
14
15
  secret=File.read(options[:key])
15
16
  end
16
- puts @client.set_credential(service,name,value,secret)
17
+ puts @client.create_credential(service,name,value,secret,options[:organization])
17
18
  end
18
19
 
19
20
  desc "list", "get all of the credential"
20
21
  def list()
21
- puts @client.get_credentials()
22
+ @client.get_credentials["value"].each do |service,values|
23
+ # puts "#{credential['service']} : #{credential['name']} (#{credential['slug']}): #{credential['value']}"
24
+ puts "#{service}:"
25
+ values.each do |key,values|
26
+ # puts "#{key['value']} (#{value['slug']}) : #{value}"
27
+ puts " #{key} (#{values['slug']}): ***"
28
+ end
29
+ end
22
30
  end
23
31
 
24
- desc "remove SERVICE NAME", "remove a value from the credentials bag"
25
- def remove(service,name)
26
- puts @client.remove_credential(service,name)
32
+ desc "delete SERVICE NAME", "remove a value from the credentials bag"
33
+ def delete(service,name)
34
+ puts @client.delete_credential(service,name)
27
35
  end
28
36
 
29
37
  end
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
2
  require 'cli/command'
3
- require 'cli/server_task'
4
3
  require 'cli/workflow_task'
5
4
  require 'cli/credential_task'
6
5
  require 'cli/channel_task'
@@ -14,15 +13,14 @@ module Factor
14
13
  # method_option :token, :alias=>"-t", :type=>:string, :desc=>"Token value to set", :required=>true
15
14
  def login(email,token)
16
15
  config = get_config
17
- config[:email]=email
18
- config[:token]=token
16
+ config['email']=email
17
+ config['token']=token
19
18
  save_config(config)
20
19
  end
21
20
  end
22
21
  end
23
22
  end
24
23
 
25
- Factor::CLI::FactorTask.register(Factor::CLI::ServerTask, "server","server","start and list servers")
26
24
  Factor::CLI::FactorTask.register(Factor::CLI::WorkflowTask,"workflow","workflow","start and list workflows")
27
25
  Factor::CLI::FactorTask.register(Factor::CLI::ChannelTask,"channel","channel","install,uninstall, list channels")
28
26
  Factor::CLI::FactorTask.register(Factor::CLI::CredentialTask,"credential","credential","manage remote credential store")
@@ -4,53 +4,43 @@ require 'cli/command'
4
4
  module Factor
5
5
  module CLI
6
6
  class WorkflowTask < Command
7
-
8
- desc "call WORKFLOW","start a workflow"
9
- method_option :parameters, :type=>:hash, :default=>{}, :required=>false
10
- def call(workflow_name)
11
- puts "starting workflow #{workflow_name} with options #{options.parameters.to_s}"
12
-
13
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
14
- id = engine.send_start_workflow(workflow_name,options.parameters)
15
-
16
- puts "workflow executed with id #{id}"
17
- end
18
-
19
- desc "listen WORKFLOW","start listener for workflow"
20
- def listen(workflow_name)
21
- puts "starting listener #{workflow_name}"
22
-
23
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
24
- engine.send_start_listener(workflow_name)
7
+
8
+ desc "start WORKFLOW","start listener for workflow"
9
+ def start(workflow_name)
10
+ puts @client.start_workflow(workflow_name)["notice"]
25
11
  end
26
12
 
27
13
  desc "stop WORKFLOW","stop listener for workflow"
28
14
  def stop(workflow_name)
29
- puts "starting listener #{workflow_name}"
30
-
31
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
32
- engine.send_stop_listener(workflow_name)
15
+ puts @client.stop_workflow(workflow_name)["notice"]
16
+ end
17
+
18
+ desc "call WORKFLOW","call workflow"
19
+ method_option :parameters, :type=>:hash, :default=>{}, :required=>false
20
+ def call(workflow_name)
21
+ puts @client.call_workflow(workflow_name,options[:parameters])["notice"]
33
22
  end
34
23
 
35
24
 
36
25
  desc "list", "list all the workflows"
37
- #method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
38
26
  def list
39
- puts @client.get_workflows
27
+ @client.get_workflows().each do |workflow|
28
+ puts "#{workflow['name']} (#{workflow['slug']})"
29
+ end
40
30
  end
41
31
 
42
- desc "add NAME FILENAME", "add a key and value for the credential"
32
+ desc "create NAME FILENAME", "add a key and value for the credential"
43
33
  #method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
44
- #method_option :value, :alias=>"-v", :type=>:string, :desc=>"values"
45
- def add(name,filename)
34
+ method_option :organization, :alias=>"-v", :type=>:string, :desc=>"Organizoation to which this workflow belongs"
35
+ def create(name,filename)
46
36
  contents=File.open(File.expand_path(filename), "rb") {|f| f.read}
47
- puts @client.add_workflow(name,contents)
37
+ puts @client.create_workflow(name,contents,options[:organization])["notice"]
48
38
  end
49
39
 
50
- # desc "remove NAME", "remove a workflow"
51
- # def remove(name)
52
- # puts @client.remove_workflow(name)
53
- # end
40
+ desc "delete NAME", "remove a workflow"
41
+ def delete(name)
42
+ puts @client.delete_workflow(name)
43
+ end
54
44
 
55
45
 
56
46
 
data/lib/client/client.rb CHANGED
@@ -7,6 +7,7 @@ require 'open-uri'
7
7
  require 'digest/sha2'
8
8
  require 'openssl'
9
9
  require 'base64'
10
+ require 'json'
10
11
 
11
12
 
12
13
  module Factor
@@ -19,102 +20,11 @@ module Factor
19
20
  @host=host
20
21
  end
21
22
 
22
- def register(email,password)
23
- end
24
-
25
- def login(email, password)
26
- end
27
-
28
23
  def login_token(token)
29
24
  @token=token
30
25
  end
31
26
 
32
- def load_workflows(engine,&code)
33
- code.call("downloading workflow list")
34
- workflows = rest_get("workflows")
35
- code.call("downloaded #{workflows.size} workflows")
36
- workflows.each do |workflow_info|
37
- code.call("parsing '#{workflow_info['name']}'")
38
- workflow_definition = JSON.parse(workflow_info['definition'])
39
- code.call("loading '#{workflow_info['name']}' into runtime")
40
- workflow=Factor::Runtime::Workflow.new(workflow_definition)
41
- engine.load_workflow(workflow)
42
- code.call("loading complete for '#{workflow_info['name']}'")
43
- end
44
- engine
45
- end
46
-
47
- def load_credentials(engine,secret=nil,&code)
48
- code.call("downloading credential list")
49
- credentials = rest_get("credentials")["value"]
50
- code.call("loading credentials")
51
-
52
- if secret
53
- code.call("decrypting credentials")
54
- decrypter = OpenSSL::Cipher.new("AES-256-CFB")
55
- sha256= Digest::SHA2.new(256)
56
- decrypter.key=Base64.encode64(sha256.digest(secret))
57
- decrypter.decrypt
58
- credentials.each do |service,creds|
59
- creds.each do |credential,value|
60
- if value["encrypted"]
61
- decrypted = decrypter.update(Base64.decode64(value["value"])) + decrypter.final
62
- credentials[service][credential]["value"]=decrypted
63
- credentials[service][credential]["encrypted"]=false
64
- end
65
- end
66
- end
67
- code.call("decrypting credentials complete")
68
- end
69
-
70
- engine.load_credentials(credentials)
71
-
72
- engine
73
- end
74
-
75
- def load_channels(engine,&code)
76
-
77
- # get list of channels
78
- code.call("downloading channels list")
79
- channels = rest_get("channels")
80
-
81
- #load each channel
82
- channels.each do |channel|
83
- if !channel['zip_url'].empty?
84
- code.call("downloading '#{channel['zip_url']}'")
85
- # URI of the zip file containing the module
86
- uri = URI.parse(channel['zip_url'])
87
-
88
- # temp file to store the zip for download
89
- #temp_file = Tempfile.new([uri.path.gsub(/\W+/,'-'), '.zip'], :encoding => 'ascii-8bit')
90
- temp_file = Tempfile.new([uri.to_s.gsub(/\W+/,'-'), '.zip'])
91
-
92
- # temp directory where zip will be decompressed
93
- unzip_dir=temp_file.path[0..-5]
94
-
95
- # download
96
- open(uri.to_s,"rb") do |bin|
97
- temp_file.print bin.read
98
- end
99
- temp_file.close
100
-
101
- code.call("unzipping '#{channel['name']}'")
102
- # unzip download zip into unzipped directory
103
- unzip(temp_file.path,unzip_dir)
104
-
105
- filename = unzip_dir + "/" + channel['module_name'].split(/(?=[A-Z])/).map{ |x| x.downcase }.join('_') + ".rb"
106
-
107
- code.call("loading '#{channel["name"]}' into engine")
108
- engine.load_channel(filename, channel)
109
- end
110
- end
111
-
112
-
113
- engine
114
- end
115
-
116
-
117
- def set_credential(service,name,value,secret=nil)
27
+ def create_credential(service,name,value,secret=nil,organization=nil)
118
28
  # this is a PUT not POST because it is technically editing, not creating a new one
119
29
  credential = {:service=>service,:name=>name,:value=>value}
120
30
 
@@ -129,49 +39,65 @@ module Factor
129
39
  credential[:value]=encrypted
130
40
  credential[:encrypted]=true
131
41
  end
132
-
133
- rest_post("credentials",credential)
42
+ path = !!organization ? "organizations/#{organization}/credentials" : "credentials"
43
+ rest_post(path,credential)
134
44
  end
135
45
 
136
46
  def get_credentials()
137
47
  rest_get("credentials")
138
48
  end
139
49
 
140
- def remove_credential(service,name)
50
+ def delete_credential(service,name)
141
51
  rest_delete("credentials",{:service=>service,:name=>name})
142
52
  end
143
53
 
144
54
 
145
55
 
146
56
 
147
- # workflows
148
- def add_workflow(key,definition)
149
- rest_post("workflows",{:workflow=>{:name=>key,:definition=>definition}})
57
+ # untested
58
+ def create_workflow(key,definition,organization=nil)
59
+ path = !!organization ? "organizations/#{organization}/workflows" : "workflows"
60
+ rest_post(path,{:workflow=>{:name=>key,:definition=>definition}})
150
61
  end
151
62
 
152
63
  def get_workflows()
153
64
  rest_get("workflows")
154
65
  end
155
66
 
156
- # def remove_workflow(name="")
157
- # rest_delete("workflows",{:name=>name})
158
- # end
67
+ def delete_workflow(workflow_name)
68
+ rest_delete("workflows/#{workflow_name}")
69
+ end
70
+
71
+ def start_workflow(workflow_name)
72
+ rest_get("workflows/#{workflow_name}/state")
73
+ end
74
+
75
+ def stop_workflow(workflow_name)
76
+ rest_delete("workflows/#{workflow_name}/state")
77
+ end
78
+
79
+ def call_workflow(workflow_name,params)
80
+ rest_get("workflows/#{workflow_name}/call",params)
81
+ end
82
+
159
83
 
160
84
 
161
85
  # channels
162
- def add_channel(path,definition_file)
86
+ def create_channel(path,definition_file,organization=nil)
163
87
  file=zip(File.expand_path(path))
164
88
  definition = File.read(definition_file)
165
- rest_post("channels",{:zip=>file,:channel=>{:definition=>definition}})
89
+
90
+ path = !!organization ? "organizations/#{organization}/channels" : "channels"
91
+ rest_post(path,{:zip=>file,:channel=>{:definition=>definition}})
166
92
  end
167
93
 
168
94
  def get_channels()
169
95
  rest_get("channels")
170
96
  end
171
97
 
172
- # def remove_channel(name="")
173
- # rest_delete("channels",{:name=>name})
174
- # end
98
+ def delete_channel(channel_name)
99
+ rest_delete("channels/#{channel_name}")
100
+ end
175
101
 
176
102
 
177
103
 
@@ -180,37 +106,6 @@ module Factor
180
106
 
181
107
  private
182
108
 
183
- def fix_github_zip_archive(file)
184
- Zip::ZipFile.open(file.path) do |zip|
185
- basedir = zip.entries.first.name
186
- zip.remove zip.entries.first
187
- zip.each do |entry|
188
- target = entry.name.sub(/^#{basedir}/,'')
189
- if entry.directory?
190
- zip.dir.mkdir(target)
191
- else
192
- zip.file.open(entry.name) do |istream|
193
- zip.file.open(target, 'w') do |ostream|
194
- ostream.write istream.read
195
- end
196
- end
197
- end
198
- zip.remove entry
199
- end
200
- end
201
- end
202
-
203
- def unzip(zip, unzip_dir, remove_after = false)
204
- Zip::ZipFile.open(zip) do |zip_file|
205
- zip_file.each do |f|
206
- f_path=File.join(unzip_dir, f.name)
207
- FileUtils.mkdir_p(File.dirname(f_path))
208
- zip_file.extract(f, f_path) unless File.exist?(f_path)
209
- end
210
- end
211
- FileUtils.rm(zip) if remove_after
212
- end
213
-
214
109
  def zip(directory)
215
110
  path=directory.dup
216
111
  path.sub!(%r[/$],'')
@@ -223,35 +118,34 @@ module Factor
223
118
  end
224
119
  File.open(zip_path,'r')
225
120
  end
226
-
121
+
227
122
  def rest_get(path,params={})
228
123
  params["auth_token"]=@token
229
124
 
230
125
  param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
231
-
232
- JSON.parse(RestClient.get("http://#{@host}/#{path}.json?#{param_string}"))
126
+ response=RestClient.get("http://#{@host}/#{path}.json?#{param_string}")
127
+ JSON.parse(response)
233
128
  end
234
129
 
235
130
  def rest_put(path,params={})
236
131
  params["auth_token"]=@token
237
-
238
- JSON.parse(RestClient.put("http://#{@host}/#{path}.json",params))
132
+ response=RestClient.put("http://#{@host}/#{path}.json",params)
133
+ JSON.parse(response)
239
134
  end
240
135
 
241
136
  def rest_post(path,params={})
242
- #params["auth_token"]=@token
243
-
244
- JSON.parse(RestClient.post("http://#{@host}/#{path}.json?auth_token=#{@token}",params))
137
+ response=RestClient.post("http://#{@host}/#{path}.json?auth_token=#{@token}",params)
138
+ JSON.parse(response)
245
139
  end
246
140
 
247
141
  def rest_delete(path,params={})
248
142
  params["auth_token"]=@token
249
143
 
250
144
  param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
251
-
252
- JSON.parse(RestClient.delete("http://#{@host}/#{path}.json?#{param_string}"))
145
+ response=RestClient.delete("http://#{@host}/#{path}.json?#{param_string}")
146
+ JSON.parse(response)
253
147
  end
254
148
 
255
149
  end
256
150
  end
257
- end
151
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -59,38 +59,6 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: 2.0.2
62
- - !ruby/object:Gem::Dependency
63
- name: mustache
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: 0.99.4
70
- type: :runtime
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: 0.99.4
78
- - !ruby/object:Gem::Dependency
79
- name: eventmachine
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: 0.12.10
86
- type: :runtime
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: 0.12.10
94
62
  - !ruby/object:Gem::Dependency
95
63
  name: json
96
64
  requirement: !ruby/object:Gem::Requirement
@@ -107,22 +75,6 @@ dependencies:
107
75
  - - ! '>='
108
76
  - !ruby/object:Gem::Version
109
77
  version: 1.7.6
110
- - !ruby/object:Gem::Dependency
111
- name: amqp
112
- requirement: !ruby/object:Gem::Requirement
113
- none: false
114
- requirements:
115
- - - ! '>='
116
- - !ruby/object:Gem::Version
117
- version: 0.9.8
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
- requirements:
123
- - - ! '>='
124
- - !ruby/object:Gem::Version
125
- version: 0.9.8
126
78
  description: Friendly command-line interface and library for Factor
127
79
  email: maciej@factor.io
128
80
  executables:
@@ -139,17 +91,9 @@ files:
139
91
  - lib/cli/command.rb
140
92
  - lib/cli/credential_task.rb
141
93
  - lib/cli/factor_task.rb
142
- - lib/cli/server_task.rb
143
94
  - lib/cli/workflow_task.rb
144
95
  - lib/client/client.rb
145
96
  - lib/factor.rb
146
- - lib/runtime/attributes.rb
147
- - lib/runtime/engine.rb
148
- - lib/runtime/listener_message.rb
149
- - lib/runtime/message_bus.rb
150
- - lib/runtime/workflow.rb
151
- - lib/runtime/workflow_instance.rb
152
- - lib/runtime/workflow_step_messsage.rb
153
97
  - bin/factor
154
98
  homepage: http://rubygems.org/gems/factor
155
99
  licenses: []
@@ -172,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
116
  version: '0'
173
117
  requirements: []
174
118
  rubyforge_project:
175
- rubygems_version: 1.8.24
119
+ rubygems_version: 1.8.25
176
120
  signing_key:
177
121
  specification_version: 3
178
122
  summary: Factor.io Library and CLI
@@ -1,82 +0,0 @@
1
- require 'rubygems'
2
- require 'cli/command'
3
-
4
- module Factor
5
- module CLI
6
- class ServerTask < Command
7
- desc "start", "start the server"
8
- method_option :tags, :alias=>"-t", :type=>:hash, :desc=>"Optional tags to identify from workflow"
9
- method_option :channels, :type=>:array, :desc=>"Optional channel ruby file list for development"
10
- method_option :verbose, :type=>:boolean, :desc=>"Display everything"
11
- method_option :key, :type=>:string, :desc=>"File reference containing the symmetric key for encryption"
12
- def start
13
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
14
-
15
- options.tags.each {|tag,value| engine.tag(tag,value)} if options.tags?
16
-
17
- # load channels from server
18
- say "loading channels from server" if options.verbose?
19
- engine = @client.load_channels(engine) do |message|
20
- say " #{message}" if options.verbose?
21
- end
22
-
23
- # load channels from files
24
- if options.channels?
25
- options.channels.each do |file|
26
- full_file=File.expand_path(file)
27
- say " loading '#{full_file}'" if options.verbose?
28
- engine.load_channel(full_file)
29
- say " loaded '#{full_file}'" if options.verbose?
30
- end
31
- end
32
-
33
- say "loading channels complete" if options.verbose?
34
-
35
-
36
- # load workflows from server
37
- say "loading workflows from server" if options.verbose?
38
- engine = @client.load_workflows(engine) do |message|
39
- say " #{message}" if options.verbose?
40
- end
41
- say "loading workflows complete" if options.verbose?
42
-
43
- say "loading credentials from server" if options.verbose?
44
- securet=nil
45
- if options[:key]
46
- secret=File.read(options[:key])
47
- end
48
- engine = @client.load_credentials(engine,secret) do |message|
49
- say " #{message}" if options.verbose?
50
- end
51
- say "loading credentials complete" if options.verbose?
52
-
53
- say "starting the server...", :green
54
- message = engine.start
55
- if message.is_a?(Exception)
56
- say "[Unknown exception]", :red
57
- say " message:#{message}", :red
58
- say " backtrace:", :red
59
- message.backtrace.each {|line| say " #{line}", :red}
60
- else
61
- say "disconnecting...", :green
62
- end
63
-
64
- end
65
-
66
- desc "list", "list all the running servers"
67
- def list
68
- say "listing all servers"
69
- end
70
-
71
- desc "logs", "listen to incoming logs"
72
- def logs
73
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
74
- say "Listening...", :green
75
- engine.logs do |message|
76
- say "[#{message.route}] #{JSON.pretty_generate(message.body)}"
77
- end
78
- end
79
-
80
- end
81
- end
82
- end
@@ -1,26 +0,0 @@
1
- require 'rubygems'
2
-
3
- module Factor
4
- module Runtime
5
-
6
- class Attributes < Hash
7
-
8
- private
9
- def map_values(map,values)
10
- params=Hash.new
11
- map.each do |key,reference|
12
- params[key]=values[reference]
13
- end
14
- params
15
- end
16
-
17
- def format_map(parent,map)
18
- newmap=Hash.new
19
- map.each do |key,value|
20
- newmap["#{parent}::#{key}"]=value
21
- end
22
- newmap
23
- end
24
- end
25
- end
26
- end
@@ -1,256 +0,0 @@
1
- require 'rubygems'
2
- require 'factor'
3
- require 'mustache'
4
- #require 'facter'
5
-
6
-
7
- module Factor
8
- module Runtime
9
- class Engine
10
- attr_accessor :channel_modules, :workflows, :message_bus, :listeners
11
-
12
- # Engine needs modules that contain the code, workflows to run, and message bus for communication
13
- def initialize(username,token)
14
- @channel_modules={}
15
- @channel_definitions=[]
16
- @workflows = {}
17
- @message_bus = MessageBus.new(username,token)
18
- @credentials = {}
19
- @tags = {}
20
- @listeners={}
21
- end
22
-
23
- def tag(key,value)
24
- @tags[key]=value
25
- end
26
-
27
- # load the channel by referencing the .rb file
28
- # the filename is lowercase with "_" for spaces
29
- # and the module inside must be camal cased to match
30
- # once loaded it is in the channel_modules Hash
31
- def load_channel filename, definition
32
- filename = File.absolute_path(File.expand_path(filename.path)) if filename.is_a?(File) # just in case someone passes in a File not a String (i.e. me)
33
- require filename
34
- # channel_module_name = File.basename(filename).gsub('.rb','').split('_').map{|ea| ea.capitalize}.join('')
35
- channel_module_name = definition['module_name']
36
- channel_module= self.class.const_get(channel_module_name)
37
- @channel_modules[channel_module_name]=channel_module
38
- @channel_definitions << definition
39
- end
40
-
41
- def load_credentials credentials,secret=nil
42
- credentials.each do |service_name,services|
43
- @credentials[service_name] = {} if !@credentials.include?(service_name)
44
- services.each do |credential_name,credential|
45
- @credentials[service_name][credential_name] = credential["value"]
46
- end
47
- end
48
-
49
- # @credentials["credentials"] = credentials
50
- end
51
-
52
- # adds the workflow to the workflows list
53
- # the object must be a Workflow type
54
- def load_workflow workflow
55
- @workflows[workflow.name] = workflow
56
- end
57
-
58
- def logs routing_key="#", &code
59
- @message_bus.start do
60
- @message_bus.listen routing_key do |message|
61
- code.call message
62
- end
63
- end
64
- end
65
-
66
- def send_start_workflow workflow, params, keep_open=false
67
- instance_id=SecureRandom.hex
68
-
69
- @message_bus.start do
70
- message = WorkflowStepMessage.new
71
- message.position << "start"
72
- message.workflow=workflow
73
- message.add_values params
74
- message.workflow_instance_id= instance_id
75
- @message_bus.send message, !keep_open
76
- end
77
- instance_id
78
- end
79
-
80
- def send_start_listener workflow_name, keep_open=false
81
- @message_bus.start do
82
- message = ListenerMessage.new("start",workflow_name)
83
- @message_bus.send message, !keep_open
84
- end
85
- end
86
-
87
- def send_stop_listener workflow_name, keep_open=false
88
- @message_bus.start do
89
- message = ListenerMessage.new("stop",workflow_name)
90
- @message_bus.send message, !keep_open
91
- end
92
- end
93
-
94
- # start your engines. vroom vrooooom!
95
- def start
96
- begin
97
- @message_bus.start do
98
- @message_bus.listen("listener") do |payload|
99
- message = ListenerMessage.new
100
- message.from_queue(payload)
101
-
102
- listener=@workflows[message.workflow].definition["listener"]
103
- params = render_template(listener["params"],@credentials)
104
-
105
- if message.command=="start"
106
- start_listening(listener["channel"],listener["name"],listener["event"],message.workflow,params)
107
- else
108
- stop_listening(listener["channel"],listener["name"],listener["event"],message.workflow)
109
- end
110
- end
111
-
112
- @message_bus.listen("workflow") do |payload|
113
- step = WorkflowStepMessage.new
114
- step.from_queue(payload)
115
- if @workflows.include? step.workflow
116
- workflow = @workflows[step.workflow]
117
- activity = workflow.get_activity(step.position)
118
- if !activity.nil?
119
- action = activity["action"]
120
- channel = activity["channel"]
121
- target = activity["target"]
122
- params_template = activity["params"]
123
-
124
- # if match(target)
125
- values = step.body.merge(@credentials)
126
- # puts "Values: #{values.inspect}"
127
-
128
- # this maps the input values passed in with the templated defined in the workflow
129
- params = render_template(params_template,values)
130
- puts "content: #{params}"
131
- event = call_channel_method(channel,action,params)
132
- response_message = step.respond(event.params,event.class.name.split("::").last)
133
-
134
- @message_bus.send response_message
135
- # end
136
- else
137
- puts "[error] no activity found for position '#{step.position}'"
138
- end
139
- else
140
- # workflow doesn't exist
141
- puts "[warning] '#{step.workflow}' workflow wasn't found"
142
- end
143
- end
144
- end
145
- rescue SystemExit, Interrupt
146
- puts "done"
147
- rescue Exception => ex
148
- puts ex
149
- end
150
- end
151
-
152
- def call_channel_method(channel_name,action_name,params)
153
- channel_module_name = get_channel_module(channel_name)
154
- channel_module = @channel_modules[channel_module_name]
155
- action_class_name = get_class_name("actions",channel_name,action_name)
156
- command = channel_module.const_get(action_class_name)
157
- command.new.do_work(params)
158
- end
159
-
160
- def start_listening(channel_name,listener_name,event,workflow_name,params)
161
- channel_module_name = get_channel_module(channel_name)
162
- channel_module = @channel_modules[channel_module_name]
163
- listener_class_name = get_class_name("listeners",channel_name,listener_name)
164
- command = channel_module.const_get(listener_class_name)
165
- job_info = {:channel_name=>channel_name,:listener_name=>listener_name,:workflow_name=>workflow_name,:event=>event}.to_json
166
-
167
- @job = Thread.new do
168
- puts "Creating listener #{channel_name}::#{listener_name} on event '#{event}' => #{workflow_name}"
169
- listener=command.new(workflow_name,event)
170
-
171
- puts "Starting listener thread #{params}"
172
- listener.start params do |event_params|
173
- send_start_workflow workflow_name, event_params
174
- end
175
- end
176
- @listeners[job_info]=@job
177
- end
178
-
179
- def stop_listening(channel_name,listener_name,event,workflow_name)
180
- # channel_module_name = get_channel_module(channel_name)
181
- # channel_module = @channel_modules[channel_module_name]
182
- # listener_class = get_class_name("listeners",channel_name,listener_name)
183
- # command = channel_module.const_get(listener_name)
184
- job_info = {:channel_name=>channel_name,:listener_name=>listener_name,:workflow_name=>workflow_name,:event=>event}.to_json
185
-
186
- @listeners[job_info].kill if @listeners.include?(job_info)
187
-
188
- end
189
-
190
- private
191
-
192
- def get_channel_module(channel_name)
193
- @channel_definitions.select { |channel_definition| channel_definition['name']==channel_name }.first['module_name']
194
- end
195
-
196
- # def get_action_class(channel_name,action_name)
197
- # @channel_definitions.each do |channel_definition|
198
- # if channel_definition['name']==channel_name
199
- # channel_definition['actions'].each do |action|
200
- # return action["class_name"] if action['name']==action_name
201
- # end
202
- # end
203
- # end
204
- # end
205
-
206
- # def get_listener_class(channel_name,listener_name)
207
- # @channel_definitions.each do |channel_definition|
208
- # if channel_definition['name']==channel_name
209
- # channel_definition['listeners'].each do |listener|
210
- # return listener["class_name"] if listener['name']==listener_name
211
- # end
212
- # end
213
- # end
214
- # end
215
-
216
- def get_class_name(type,channel_name,class_name)
217
- @channel_definitions.each do |channel_definition|
218
- if channel_definition['name']==channel_name
219
- channel_definition[type].each do |definition|
220
- return definition["class_name"] if definition['name']==class_name
221
- end
222
- end
223
- end
224
- end
225
-
226
- def render_template(source,values)
227
- #params = Hash.new
228
-
229
- if source.is_a?(Hash)
230
- params = Hash.new
231
- source.each do |key,template|
232
- params[key]=render_template(template,values)
233
- end
234
- else
235
- params = Mustache.render(source,values)
236
- end
237
-
238
- # activity_params.each do |key,template|
239
- # params[key]=Mustache.render(template,values)
240
- # end
241
- params
242
- end
243
-
244
- # def match(target)
245
- # return true if target==nil || target==""
246
- # facts={}
247
- # #Facter.each {|k,v| facts[k]=v}
248
- # @tags.each {|k,v| facts[k]=v}
249
- # key,value=target.split("=")
250
- # facts[key]==value
251
- # end
252
-
253
-
254
- end
255
- end
256
- end
@@ -1,42 +0,0 @@
1
- require 'rubygems'
2
- require 'json/ext'
3
-
4
- module Factor
5
- module Runtime
6
- class ListenerMessage
7
- attr_accessor :command,:workflow
8
- def initialize(command=nil,workflow=nil)
9
- @command=command
10
- @workflow=workflow
11
- end
12
-
13
-
14
- def route
15
- # "#{workflow}.#{position.join('.')}"
16
- "listener"
17
- end
18
-
19
- def payload
20
- JSON.generator = JSON::Ext::Generator
21
- obj = {"command"=>@command, "workflow"=>@workflow}
22
- JSON.generate(obj)
23
- end
24
-
25
- def from_queue payload
26
- message=JSON.parse(payload)
27
- @command=message["command"]
28
- @workflow=message["workflow"]
29
- end
30
- # def from_queue payload
31
- #
32
- # @workflow = message["workflow"]
33
- # @position=message["position"]
34
- # @body=message["body"]
35
- # @workflow_instance_id=message["workflow_instance_id"]
36
- # @step_id=message["step_id"]
37
- # @last_step_id=message["last_step_id"]
38
- # end
39
-
40
- end
41
- end
42
- end
@@ -1,59 +0,0 @@
1
- require 'rubygems'
2
- require 'eventmachine'
3
- require 'amqp'
4
-
5
- module Factor
6
- module Runtime
7
- class MessageBus
8
- attr_accessor :host, :vhost, :username, :token, :connection, :channel, :exchange, :queue
9
-
10
- def initialize(email,token)
11
- @host = "queue.factor.io"
12
- @vhost = email
13
- @username=email
14
- @token=token
15
- end
16
-
17
-
18
- # Creates the connection and creates a topic exchange
19
- # An exchange references a place to send messages to
20
- # the exchange routes it to the queues based on the route_key
21
- def start(topic="factor",&code)
22
- EventMachine.run do
23
- #connection_settings={:host=>@host,:user=>@username,:password=>@token,:vhost=>@vhost}
24
- connection_settings={:host=>@host}
25
- @connection = AMQP.connect(connection_settings)
26
- @channel = AMQP::Channel.new(connection)
27
- @exchange = @channel.topic(topic,:auto_delete=>true) # new topic exchange
28
- code.call
29
- end
30
- end
31
-
32
- # creates a new queue to listen to the topic exchange
33
- def listen(routing_key="#",&code)
34
- queue_name=SecureRandom.hex
35
- @queue = @channel.queue(queue_name)
36
- @queue.bind(@exchange, :routing_key=>routing_key) # bind queue to the Exchange
37
-
38
- @queue.subscribe do |headers,payload|
39
- # code.call(headers.routing_key,payload)
40
- puts "[Received Message (#{headers.routing_key})] #{payload.inspect}"
41
- code.call(payload)
42
- end
43
- end
44
-
45
- def send(message,close=false)
46
- puts "[Sending Message (#{message.route})] #{message.payload.inspect}"
47
- @exchange.publish(message.payload,:routing_key => message.route)
48
- EM.add_timer(1, Proc.new { close}) if close
49
- end
50
-
51
- def close
52
- @connection.close{ EventMachine.stop }
53
-
54
- end
55
-
56
- end
57
-
58
- end
59
- end
@@ -1,25 +0,0 @@
1
- require 'rubygems'
2
-
3
- module Factor
4
- module Runtime
5
- class Workflow
6
- attr_accessor :definition
7
- attr_reader :name
8
-
9
- def initialize(definition)
10
- @definition=definition
11
- @name = @definition["name"]
12
- end
13
-
14
- def get_activity(position)
15
- last_position=@definition
16
- position.each do |section|
17
- return nil if last_position[section].nil?
18
- last_position=last_position[section]
19
- end
20
- last_position
21
- end
22
-
23
- end
24
- end
25
- end
@@ -1,28 +0,0 @@
1
- require 'rubygems'
2
-
3
- module Factor
4
- module Runtime
5
- class WorkflowInstance
6
- attr_accessor :id, :attributes, :start_message, :workflow
7
-
8
- def initialize(workflow,attributes)
9
- @id=SecureRandom.hex
10
- @attributes = attributes
11
- @workflow = workflow
12
- @start_message = Message.new(@attributes)
13
- @start_message.workflow_instance_id=@id
14
- @start_message.position=["start"]
15
- end
16
-
17
- def get_activity(position)
18
- last_position=@workflow
19
- position.each do |section|
20
- return nil if last_position[section].nil?
21
- last_position=last_position[section]
22
- end
23
- last_position
24
- end
25
-
26
- end
27
- end
28
- end
@@ -1,63 +0,0 @@
1
- require 'rubygems'
2
- require 'json/ext'
3
-
4
- module Factor
5
- module Runtime
6
- class WorkflowStepMessage
7
- attr_accessor :body, :workflow, :workflow_instance_id, :step_id, :last_step_id, :position
8
- def initialize
9
- @step_id=SecureRandom.hex
10
- # @workflow = workflow
11
- @body = Hash.new
12
- @position = Array.new
13
- end
14
-
15
- def respond(params, event)
16
- m = WorkflowStepMessage.new
17
- m.body = @body
18
- m.workflow_instance_id=@workflow_instance_id
19
- m.last_step_id = @step_id
20
- m.workflow = @workflow
21
- m.position = @position
22
- m.position << "on"
23
- m.position << event
24
- m.add_values(params)
25
- m
26
- end
27
-
28
- def add_values(values)
29
- current=@body
30
- position.each do |key|
31
- current[key]={} if !current.include?(key)
32
- current=current[key]
33
- end
34
- values.each do |key,value|
35
- current[key]=value
36
- end
37
-
38
- end
39
-
40
- def route
41
- # "#{workflow}.#{position.join('.')}"
42
- "workflow"
43
- end
44
-
45
- def payload
46
- JSON.generator = JSON::Ext::Generator
47
- obj = {"body"=>@body, "workflow_instance_id"=>@workflow_instance_id, "step_id"=>@step_id, "last_step_id"=>@last_step_id, "workflow"=>@workflow,"position"=>@position}
48
- JSON.generate(obj)
49
- end
50
-
51
- def from_queue payload
52
- message=JSON.parse(payload)
53
- @workflow = message["workflow"]
54
- @position=message["position"]
55
- @body=message["body"]
56
- @workflow_instance_id=message["workflow_instance_id"]
57
- @step_id=message["step_id"]
58
- @last_step_id=message["last_step_id"]
59
- end
60
-
61
- end
62
- end
63
- end