factor 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/factor +1 -1
- data/lib/{factor.rb → commands.rb} +8 -8
- data/lib/commands/base.rb +6 -64
- data/lib/commands/{registry.rb → registry_command.rb} +1 -1
- data/lib/commands/{workflows.rb → workflow_command.rb} +25 -34
- data/lib/common/deep_struct.rb +48 -0
- data/lib/factor/version.rb +1 -1
- data/lib/logger/basic.rb +68 -0
- data/lib/logger/logger.rb +28 -0
- data/lib/runtime/exec_handler.rb +16 -0
- data/lib/runtime/service_address.rb +46 -0
- data/lib/runtime/service_caller.rb +105 -0
- data/lib/runtime/workflow.rb +170 -0
- data/lib/websocket_manager.rb +2 -1
- data/spec/spec_helper.rb +0 -7
- metadata +14 -17
- data/lib/listener.rb +0 -26
- data/lib/runtime.rb +0 -232
- data/spec/base_spec.rb +0 -102
- data/spec/listener_spec.rb +0 -9
- data/spec/registry_spec.rb +0 -0
- data/spec/workflow_spec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26eb148636a08597d3d15057a3536d5b1780f9c8
|
4
|
+
data.tar.gz: 87b420da894fa98fd2f9e151982a63008ea452cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90c063260db5e9fc933991d4bedfb17b52c52e1908f5fbb0a1ab53fd5acfbf03eca4027947b99c854aea968fbfd732f7db69d3d1096c2fe6783be2a37e665d37
|
7
|
+
data.tar.gz: fb996d555b5542c5dda46e198e2fb84cf8a642308843a4d4edaa2946f4985e3ff68b5058b4fa495c952fc18a832acddc47f04406b469415bce1536002278bb58
|
data/bin/factor
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require 'commander/import'
|
4
4
|
|
5
5
|
require 'factor/version'
|
6
|
-
require 'commands/
|
7
|
-
require 'commands/
|
6
|
+
require 'commands/workflow_command'
|
7
|
+
require 'commands/registry_command'
|
8
8
|
|
9
9
|
program :name, 'Factor.io Server'
|
10
10
|
program :version, Factor::VERSION
|
@@ -17,20 +17,20 @@ command 'server' do |c|
|
|
17
17
|
c.option '--credentials FILE', String, 'credentials.yml file path.'
|
18
18
|
c.option '--connectors FILE', String, 'connectors.yml file path'
|
19
19
|
c.option '--path FILE', String, 'Path to workflows'
|
20
|
-
c.when_called Factor::Commands::
|
20
|
+
c.when_called Factor::Commands::WorkflowCommand, :server
|
21
21
|
end
|
22
22
|
|
23
23
|
command 'cloud' do |c|
|
24
24
|
c.syntax = 'factor host <account id> <workflow id> <api key>'
|
25
25
|
c.description = 'Start the Factor.io Server using your workflows and credentials from Factor.io Cloud'
|
26
26
|
c.option '--host URL', String, 'Use non-default Cloud service provider (e.g. pro server)'
|
27
|
-
c.when_called Factor::Commands::
|
27
|
+
c.when_called Factor::Commands::WorkflowCommand, :cloud
|
28
28
|
end
|
29
29
|
|
30
30
|
command 'registry workflows' do |c|
|
31
31
|
c.syntax = 'factor registry workflows'
|
32
32
|
c.description = 'Get list of available workflow jumpstarts'
|
33
|
-
c.when_called Factor::Commands::
|
33
|
+
c.when_called Factor::Commands::RegistryCommand, :workflows
|
34
34
|
end
|
35
35
|
|
36
36
|
command 'registry workflows add' do |c|
|
@@ -39,13 +39,13 @@ command 'registry workflows add' do |c|
|
|
39
39
|
c.option '--credentials FILE', String, 'credentials.yml file path.'
|
40
40
|
c.option '--connectors FILE', String, 'connectors.yml file path'
|
41
41
|
c.option '--values \'{"api_key":"foo"}\'', String, "{}"
|
42
|
-
c.when_called Factor::Commands::
|
42
|
+
c.when_called Factor::Commands::RegistryCommand, :add_workflow
|
43
43
|
end
|
44
44
|
|
45
45
|
command 'registry connectors' do |c|
|
46
46
|
c.syntax = 'factor registry connectors'
|
47
47
|
c.description = 'Get list of available connectors'
|
48
|
-
c.when_called Factor::Commands::
|
48
|
+
c.when_called Factor::Commands::RegistryCommand, :connectors
|
49
49
|
end
|
50
50
|
|
51
51
|
command 'registry connector add' do |c|
|
@@ -54,7 +54,7 @@ command 'registry connector add' do |c|
|
|
54
54
|
c.option '--credentials FILE', String, 'credentials.yml file path.'
|
55
55
|
c.option '--connectors FILE', String, 'connectors.yml file path'
|
56
56
|
c.option '--values \'{"api_key":"foo"}\'', String, "{}"
|
57
|
-
c.when_called Factor::Commands::
|
57
|
+
c.when_called Factor::Commands::RegistryCommand, :add_connector
|
58
58
|
end
|
59
59
|
|
60
60
|
alias_command 's', 'server'
|
data/lib/commands/base.rb
CHANGED
@@ -4,40 +4,21 @@ require 'colored'
|
|
4
4
|
require 'configatron'
|
5
5
|
require 'yaml'
|
6
6
|
require 'fileutils'
|
7
|
+
require 'logger/basic'
|
7
8
|
|
8
9
|
module Factor
|
9
10
|
module Commands
|
10
11
|
# Base command with common methods used by all commands
|
11
12
|
class Command
|
13
|
+
attr_accessor :logger
|
14
|
+
|
12
15
|
DEFAULT_FILENAME = {
|
13
16
|
connectors: File.expand_path('./connectors.yml'),
|
14
17
|
credentials: File.expand_path('./credentials.yml')
|
15
18
|
}
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
def info(options = {})
|
20
|
-
log_line :info, options
|
21
|
-
end
|
22
|
-
|
23
|
-
def error(options = {})
|
24
|
-
log_line :error, options
|
25
|
-
end
|
26
|
-
|
27
|
-
def warn(options = {})
|
28
|
-
log_line :warn, options
|
29
|
-
end
|
30
|
-
|
31
|
-
def success(options = {})
|
32
|
-
log_line :success, options
|
33
|
-
end
|
34
|
-
|
35
|
-
def exception(message, exception)
|
36
|
-
error 'message' => message
|
37
|
-
error 'message' => " #{exception.message}"
|
38
|
-
exception.backtrace.each do |line|
|
39
|
-
error 'message' => " #{line}"
|
40
|
-
end
|
20
|
+
def initialize
|
21
|
+
@logger = Factor::Log::BasicLogger.new
|
41
22
|
end
|
42
23
|
|
43
24
|
def load_config(options = {})
|
@@ -77,46 +58,7 @@ module Factor
|
|
77
58
|
end
|
78
59
|
configatron[config_type].configure_from_hash(data)
|
79
60
|
rescue => ex
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
def log_line(section, options = {})
|
84
|
-
options = { message: options } if options.is_a?(String)
|
85
|
-
tag = tag(options)
|
86
|
-
message = options['message'] || options[:message]
|
87
|
-
section_text = format_section(section)
|
88
|
-
write "[ #{section_text} ] [#{time}]#{tag} #{message}" if message
|
89
|
-
end
|
90
|
-
|
91
|
-
def format_section(section)
|
92
|
-
formated_section = section.to_s.upcase.center(10)
|
93
|
-
case section
|
94
|
-
when :error then formated_section.red
|
95
|
-
when :info then formated_section.bold
|
96
|
-
when :warn then formated_section.yellow
|
97
|
-
when :success then formated_section.green
|
98
|
-
else formated_section
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def tag(options)
|
103
|
-
primary = options['service_id'] || options['instance_id']
|
104
|
-
secondary = if options['service_id'] && options['instance_id']
|
105
|
-
":#{options['instane_id']}"
|
106
|
-
else
|
107
|
-
''
|
108
|
-
end
|
109
|
-
primary ? "[#{primary}#{secondary}]" : ''
|
110
|
-
end
|
111
|
-
|
112
|
-
def time
|
113
|
-
Time.now.localtime.strftime('%m/%d/%y %T.%L')
|
114
|
-
end
|
115
|
-
|
116
|
-
def write(message)
|
117
|
-
stream = @destination_stream || $stdout
|
118
|
-
stream.puts(message)
|
119
|
-
stream.flush
|
61
|
+
logger.error "Couldn't load #{config_type} from #{absolute_path}", exception:ex
|
120
62
|
end
|
121
63
|
end
|
122
64
|
end
|
@@ -10,7 +10,7 @@ require 'commands/base'
|
|
10
10
|
module Factor
|
11
11
|
module Commands
|
12
12
|
# Workflow is a Command to start the factor runtime from the CLI
|
13
|
-
class
|
13
|
+
class RegistryCommand < Factor::Commands::Command
|
14
14
|
|
15
15
|
def workflows(args, options)
|
16
16
|
list = get_yaml_data 'https://raw.githubusercontent.com/factor-io/index/master/workflows.yml'
|
@@ -3,14 +3,15 @@
|
|
3
3
|
require 'configatron'
|
4
4
|
|
5
5
|
require 'commands/base'
|
6
|
-
require 'runtime'
|
6
|
+
require 'runtime/workflow'
|
7
7
|
|
8
8
|
module Factor
|
9
9
|
module Commands
|
10
10
|
# Workflow is a Command to start the factor runtime from the CLI
|
11
|
-
class
|
11
|
+
class WorkflowCommand < Factor::Commands::Command
|
12
12
|
def initialize
|
13
13
|
@workflows = {}
|
14
|
+
super
|
14
15
|
end
|
15
16
|
|
16
17
|
def server(_args, options)
|
@@ -23,7 +24,7 @@ module Factor
|
|
23
24
|
load_config(config_settings)
|
24
25
|
load_all_workflows(workflow_filename)
|
25
26
|
block_until_interupt
|
26
|
-
info 'Good bye!'
|
27
|
+
logger.info 'Good bye!'
|
27
28
|
end
|
28
29
|
|
29
30
|
def cloud(args, options)
|
@@ -31,35 +32,35 @@ module Factor
|
|
31
32
|
host = (options.host || "https://factor.io").sub(/(\/)+$/,'')
|
32
33
|
|
33
34
|
if !api_key || !workflow_id || !account_id
|
34
|
-
error "API Key, Worklfow ID and Acount ID are all required"
|
35
|
+
logger.error "API Key, Worklfow ID and Acount ID are all required"
|
35
36
|
exit
|
36
37
|
end
|
37
38
|
|
38
|
-
info "Getting workflow (#{workflow_id}) from Factor.io Cloud"
|
39
|
+
logger.info "Getting workflow (#{workflow_id}) from Factor.io Cloud"
|
39
40
|
begin
|
40
41
|
workflow_url = "#{host}/#{account_id}/workflows/#{workflow_id}.json?auth_token=#{api_key}"
|
41
42
|
raw_content = RestClient.get(workflow_url)
|
42
43
|
workflow_info = JSON.parse(raw_content)
|
43
44
|
rescue => ex
|
44
|
-
error "Couldn't retreive workflow: #{ex.message}"
|
45
|
+
logger.error "Couldn't retreive workflow: #{ex.message}"
|
45
46
|
exit
|
46
47
|
end
|
47
48
|
|
48
49
|
workflow_definition = workflow_info["definition"]
|
49
50
|
|
50
|
-
info "Getting credentials from Factor.io Cloud"
|
51
|
+
logger.info "Getting credentials from Factor.io Cloud"
|
51
52
|
begin
|
52
53
|
credential_url = "#{host}/#{account_id}/credentials.json?auth_token=#{api_key}"
|
53
54
|
raw_content = RestClient.get(credential_url)
|
54
55
|
credentials = JSON.parse(raw_content)
|
55
56
|
rescue => ex
|
56
|
-
error "Couldn't retreive workflow: #{ex.message}"
|
57
|
+
logger.error "Couldn't retreive workflow: #{ex.message}"
|
57
58
|
exit
|
58
59
|
end
|
59
60
|
|
60
61
|
configatron[:credentials].configure_from_hash(credentials)
|
61
62
|
|
62
|
-
info "Getting connectors from Factor.io Cloud"
|
63
|
+
logger.info "Getting connectors from Factor.io Cloud"
|
63
64
|
connectors = {}
|
64
65
|
begin
|
65
66
|
connectors_url = "#{host}/#{account_id}/connectors.json?auth_token=#{api_key}"
|
@@ -70,7 +71,7 @@ module Factor
|
|
70
71
|
connectors[connector_id] = connector_info['connectors'].values.first
|
71
72
|
end
|
72
73
|
rescue => ex
|
73
|
-
error "Couldn't retreive workflow: #{ex.message}"
|
74
|
+
logger.error "Couldn't retreive workflow: #{ex.message}"
|
74
75
|
exit
|
75
76
|
end
|
76
77
|
|
@@ -80,7 +81,7 @@ module Factor
|
|
80
81
|
|
81
82
|
block_until_interupt
|
82
83
|
|
83
|
-
info 'Good bye!'
|
84
|
+
logger.info 'Good bye!'
|
84
85
|
end
|
85
86
|
|
86
87
|
private
|
@@ -90,22 +91,22 @@ module Factor
|
|
90
91
|
glob = "#{workflow_filename}#{glob_ending}*.rb"
|
91
92
|
file_list = Dir.glob(glob)
|
92
93
|
if !file_list.all? { |file| File.file?(file) }
|
93
|
-
error "#{workflow_filename} is neither a file or directory"
|
94
|
+
logger.error "#{workflow_filename} is neither a file or directory"
|
94
95
|
elsif file_list.count == 0
|
95
|
-
error 'No workflows in this directory to run'
|
96
|
+
logger.error 'No workflows in this directory to run'
|
96
97
|
else
|
97
98
|
file_list.each { |filename| load_workflow(File.expand_path(filename)) }
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
101
102
|
def block_until_interupt
|
102
|
-
info 'Ctrl-c to exit'
|
103
|
+
logger.info 'Ctrl-c to exit'
|
103
104
|
begin
|
104
105
|
loop do
|
105
106
|
sleep 1
|
106
107
|
end
|
107
108
|
rescue Interrupt
|
108
|
-
info 'Exiting app...'
|
109
|
+
logger.info 'Exiting app...'
|
109
110
|
ensure
|
110
111
|
@workflows.keys.each { |workflow_id| unload_workflow(workflow_id) }
|
111
112
|
end
|
@@ -113,11 +114,11 @@ module Factor
|
|
113
114
|
|
114
115
|
def load_workflow(workflow_filename)
|
115
116
|
# workflow_filename = File.expand_path(filename)
|
116
|
-
info "Loading workflow from #{workflow_filename}"
|
117
|
+
logger.info "Loading workflow from #{workflow_filename}"
|
117
118
|
begin
|
118
119
|
workflow_definition = File.read(workflow_filename)
|
119
120
|
rescue => ex
|
120
|
-
|
121
|
+
logger.error "Couldn't read file #{workflow_filename}", exception:ex
|
121
122
|
return
|
122
123
|
end
|
123
124
|
|
@@ -125,23 +126,22 @@ module Factor
|
|
125
126
|
end
|
126
127
|
|
127
128
|
def load_workflow_from_definition(workflow_definition)
|
128
|
-
info "Setting up workflow processor"
|
129
|
+
logger.info "Setting up workflow processor"
|
129
130
|
begin
|
130
131
|
connector_settings = configatron.connectors.to_hash
|
131
132
|
credential_settings = configatron.credentials.to_hash
|
132
|
-
runtime = Runtime.new(connector_settings, credential_settings)
|
133
|
-
runtime.logger = method(:log_message)
|
133
|
+
runtime = Factor::Runtime::Workflow.new(connector_settings, credential_settings, logger: logger)
|
134
134
|
rescue => ex
|
135
|
-
message = "Couldn't setup workflow process
|
136
|
-
|
135
|
+
message = "Couldn't setup workflow process"
|
136
|
+
logger.error message:message, exception:ex
|
137
137
|
end
|
138
138
|
|
139
139
|
workflow_thread = fork do
|
140
140
|
begin
|
141
|
-
info "Starting workflow"
|
141
|
+
logger.info "Starting workflow"
|
142
142
|
runtime.load(workflow_definition)
|
143
143
|
rescue => ex
|
144
|
-
|
144
|
+
logger.error message: "Couldn't workflow", exception: ex
|
145
145
|
end
|
146
146
|
end
|
147
147
|
|
@@ -149,18 +149,9 @@ module Factor
|
|
149
149
|
end
|
150
150
|
|
151
151
|
def unload_workflow(workflow_id)
|
152
|
-
info "Stopping #{workflow_id}"
|
152
|
+
logger.info "Stopping #{workflow_id}"
|
153
153
|
Process.kill('SIGINT', @workflows[workflow_id])
|
154
154
|
end
|
155
|
-
|
156
|
-
def log_message(message_info)
|
157
|
-
case message_info['status']
|
158
|
-
when 'info' then info message_info
|
159
|
-
when 'success' then success message_info
|
160
|
-
when 'warn' then warn message_info
|
161
|
-
else error message_info
|
162
|
-
end
|
163
|
-
end
|
164
155
|
end
|
165
156
|
end
|
166
157
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Factor
|
4
|
+
module Common
|
5
|
+
class DeepStruct < OpenStruct
|
6
|
+
def initialize(hash=nil)
|
7
|
+
@table = {}
|
8
|
+
@hash_table = {}
|
9
|
+
|
10
|
+
if hash
|
11
|
+
hash.each do |k,v|
|
12
|
+
@table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
|
13
|
+
@hash_table[k.to_sym] = v
|
14
|
+
|
15
|
+
new_ostruct_member(k)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h
|
21
|
+
@hash_table
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](idx)
|
25
|
+
hash = marshal_dump
|
26
|
+
hash[idx.to_sym]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.flat_hash(h,f=[],g={})
|
31
|
+
return g.update({ f=>h }) unless h.is_a? Hash
|
32
|
+
h.each { |k,r| flat_hash(r,f+[k],g) }
|
33
|
+
g
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.simple_object_convert(item)
|
37
|
+
if item.is_a?(Hash)
|
38
|
+
Factor::Common::DeepStruct.new(item)
|
39
|
+
elsif item.is_a?(Array)
|
40
|
+
item.map do |i|
|
41
|
+
simple_object_convert(i)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
item
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/factor/version.rb
CHANGED
data/lib/logger/basic.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative './logger.rb'
|
2
|
+
|
3
|
+
module Factor
|
4
|
+
module Log
|
5
|
+
class BasicLogger < Factor::Log::Logger
|
6
|
+
|
7
|
+
attr_accessor :destination_stream
|
8
|
+
|
9
|
+
def log(section, options={})
|
10
|
+
options = { message: options } if options.is_a?(String)
|
11
|
+
tag = tag(options)
|
12
|
+
message = options['message'] || options[:message]
|
13
|
+
section_text = format_section(section)
|
14
|
+
write "[ #{section_text} ] [#{time}]#{tag} #{message}" if message
|
15
|
+
exception options[:exception] if options[:exception]
|
16
|
+
end
|
17
|
+
|
18
|
+
def info(options = {})
|
19
|
+
log :info, options
|
20
|
+
end
|
21
|
+
|
22
|
+
def warn(options = {})
|
23
|
+
log :warn, options
|
24
|
+
end
|
25
|
+
|
26
|
+
def error(options = {})
|
27
|
+
log :error, options
|
28
|
+
end
|
29
|
+
|
30
|
+
def success(options = {})
|
31
|
+
log :success, options
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def exception(exception)
|
37
|
+
error message: " #{exception.message}"
|
38
|
+
exception.backtrace.each do |line|
|
39
|
+
error message: " #{line}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def format_section(section)
|
44
|
+
formated_section = section.to_s.upcase.center(10)
|
45
|
+
case section.to_sym
|
46
|
+
when :error then formated_section.red
|
47
|
+
when :info then formated_section.bold
|
48
|
+
when :warn then formated_section.yellow
|
49
|
+
when :success then formated_section.green
|
50
|
+
else formated_section
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def tag(options)
|
55
|
+
primary = options['service_id'] || options['instance_id']
|
56
|
+
secondary = ":#{options['instane_id']}" if options['service_id'] && options['instance_id']
|
57
|
+
primary ? "[#{primary}#{secondary || ''}]" : ''
|
58
|
+
end
|
59
|
+
|
60
|
+
def write(message)
|
61
|
+
stream = @destination_stream || $stdout
|
62
|
+
stream.puts(message)
|
63
|
+
stream.flush
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|