vuderacha-syrup 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +57 -0
- data/Rakefile +69 -0
- data/VERSION.yml +4 -0
- data/bin/syrup +10 -0
- data/lib/launcher.rb +10 -0
- data/lib/syrup/config_store.rb +169 -0
- data/lib/syrup/daemon.rb +58 -0
- data/lib/syrup/executor.rb +20 -0
- data/lib/syrup/fabric_support.rb +31 -0
- data/lib/syrup/fabrics/default.rb +2 -0
- data/lib/syrup/fabrics/rack.rb +4 -0
- data/lib/syrup/manager.rb +253 -0
- data/lib/syrup/runner.rb +71 -0
- data/lib/syrup.rb +211 -0
- data/spec/syrup/config_store_spec.rb +190 -0
- data/spec/syrup/runner_spec.rb +244 -0
- data/spec/syrup_spec.rb +8 -0
- metadata +73 -0
data/lib/syrup/runner.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Handler class for actually running an application
|
2
|
+
module Syrup
|
3
|
+
class Runner
|
4
|
+
# Initializes the runner with the given configuration store
|
5
|
+
def initialize(config_store, logger)
|
6
|
+
@config_store = config_store
|
7
|
+
@logger = logger
|
8
|
+
end
|
9
|
+
|
10
|
+
# Runs the application
|
11
|
+
def run(app_name)
|
12
|
+
@logger.debug "Preparing to execute #{app_name}"
|
13
|
+
app = @config_store.applications[app_name]
|
14
|
+
|
15
|
+
# Gather the properties
|
16
|
+
props = {}
|
17
|
+
# {}.merge! won't work since the config_store properties aren't a real hash...
|
18
|
+
@config_store.properties.each do |k,v| props[k] = v end
|
19
|
+
app.properties.each do |k,v| props[k] = v end
|
20
|
+
|
21
|
+
# Find the Fabric by going from app-specific to env-specific to default
|
22
|
+
fabric = app.fabric
|
23
|
+
fabric = @config_store.fabric if fabric.nil?
|
24
|
+
fabric = default_fabric if fabric.nil?
|
25
|
+
|
26
|
+
# Calculate the application executable
|
27
|
+
@app_path = app.app
|
28
|
+
|
29
|
+
# Activate all of the properties
|
30
|
+
props.each do |k,v|
|
31
|
+
@logger.debug "Applying constant #{k}=#{v}" if @verbose
|
32
|
+
Kernel.const_set k, v
|
33
|
+
end
|
34
|
+
|
35
|
+
@@active_runner = self # Record the active instance of this runner
|
36
|
+
old_argv = ARGV.clone
|
37
|
+
begin
|
38
|
+
# Overwrite ARGV
|
39
|
+
ARGV.replace(app.start_parameters)
|
40
|
+
|
41
|
+
# Execute the fabric, which should internally perform a Syrup::Runner.run_application when it has prepared
|
42
|
+
# adequately for the application to execute
|
43
|
+
load fabric
|
44
|
+
ensure
|
45
|
+
ARGV.replace(old_argv)
|
46
|
+
@@active_runner = nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def run_application
|
51
|
+
raise "Cannot execute application outside of Fabric" if @app_path.nil?
|
52
|
+
|
53
|
+
load @app_path
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.run_application
|
57
|
+
raise "Cannot execute application outside of Fabric" if @@active_runner.nil?
|
58
|
+
|
59
|
+
active.run_application
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.active
|
63
|
+
@@active_runner
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def default_fabric
|
68
|
+
File.join(File.dirname(__FILE__), 'fabrics', 'default.rb')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/syrup.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'syrup/daemon'
|
4
|
+
require 'syrup/manager'
|
5
|
+
require 'syrup/runner'
|
6
|
+
require 'syrup/fabric_support'
|
7
|
+
|
8
|
+
module Syrup
|
9
|
+
# The main application class for Syrup. Handles parsing of command line arguments, configuration, and
|
10
|
+
# delegation to the appropriate control classes.
|
11
|
+
class Application
|
12
|
+
def self.version
|
13
|
+
# Load the version from the VERSION.yml file
|
14
|
+
version = YAML::load_file(File.join(File.dirname(__FILE__), '..', 'VERSION.yml'))
|
15
|
+
|
16
|
+
"#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.logger
|
20
|
+
@logger ||= Syrup::Logger.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def run(arguments)
|
24
|
+
# Get the logger
|
25
|
+
logger = Syrup::Application.logger
|
26
|
+
|
27
|
+
# Configure defaults
|
28
|
+
@options = OpenStruct.new
|
29
|
+
@options.directory = File.expand_path('~/.syrup')
|
30
|
+
@options.verbose = false
|
31
|
+
@options.application = nil
|
32
|
+
|
33
|
+
# Parse the options
|
34
|
+
@opts = build_options
|
35
|
+
@opts.parse! arguments
|
36
|
+
|
37
|
+
# Work out what command was issued
|
38
|
+
command, command_args = determine_command(arguments)
|
39
|
+
fail "No command given" if command.nil?
|
40
|
+
|
41
|
+
# Ensure that the configured directory exists
|
42
|
+
logger.info "Configured directory as #{@options.directory}" if @options.verbose
|
43
|
+
FileUtils.mkdir_p @options.directory if not File.directory? @options.directory
|
44
|
+
|
45
|
+
# Create a manager instance
|
46
|
+
manager = Syrup::Manager.new @options.directory, logger, @options.verbose
|
47
|
+
|
48
|
+
# Handle the command
|
49
|
+
daemon = Syrup::Daemon.new @options.directory, manager
|
50
|
+
case command
|
51
|
+
when 'start'
|
52
|
+
if command_args.length < 1
|
53
|
+
manager.start_all
|
54
|
+
else
|
55
|
+
command_args.each { |arg| manager.start arg }
|
56
|
+
end
|
57
|
+
when 'stop'
|
58
|
+
if command_args.length < 1
|
59
|
+
manager.stop_all
|
60
|
+
else
|
61
|
+
manager.stop(command_args)
|
62
|
+
end
|
63
|
+
when 'restart'
|
64
|
+
if command_args.length < 1
|
65
|
+
manager.stop_all
|
66
|
+
manager.start_all
|
67
|
+
else
|
68
|
+
command_args.each { |arg| manager.stop arg }
|
69
|
+
command_args.each { |arg| manager.start arg }
|
70
|
+
end
|
71
|
+
when 'activate'
|
72
|
+
fail "No application given to activate a script for!" if command_args.length < 1
|
73
|
+
fail "No path given to activate!" if command_args.length < 2
|
74
|
+
manager.activate command_args[0], command_args[1], command_args.slice(2, command_args.length - 2)
|
75
|
+
when 'run'
|
76
|
+
# Provide the command line if provided
|
77
|
+
if command_args.length < 1
|
78
|
+
manager.run_all
|
79
|
+
else
|
80
|
+
manager.run command_args
|
81
|
+
end
|
82
|
+
# when 'run_app'
|
83
|
+
# fail('No application name provided') if command_args.length == 0
|
84
|
+
# manager.run_app command_args[0] #, command_args.slice(1, command_args.length - 1)
|
85
|
+
when 'set'
|
86
|
+
fail("No properties provided to set") if command_args.length < 1
|
87
|
+
if @options.application
|
88
|
+
manager.set_app_properties @options.application, command_args
|
89
|
+
else
|
90
|
+
manager.set_global_properties command_args
|
91
|
+
end
|
92
|
+
when 'unset'
|
93
|
+
fail("No properties provided to unset") if command_args.length < 1
|
94
|
+
if @options.application
|
95
|
+
manager.unset_app_properties @options.application, command_args
|
96
|
+
else
|
97
|
+
manager.unset_global_properties command_args
|
98
|
+
end
|
99
|
+
when 'clear'
|
100
|
+
if @options.application
|
101
|
+
manager.clear_app_properties @options.application
|
102
|
+
else
|
103
|
+
manager.clear_global_properties
|
104
|
+
end
|
105
|
+
when 'weave'
|
106
|
+
fail("No fabric provided to weave") if command_args.length < 1
|
107
|
+
if @options.application
|
108
|
+
manager.weave_for_application @options.application, command_args[0]
|
109
|
+
else
|
110
|
+
manager.weave_global command_args[0]
|
111
|
+
end
|
112
|
+
when 'unweave'
|
113
|
+
if @options.application
|
114
|
+
manager.unweave_for_application @options.application
|
115
|
+
else
|
116
|
+
manager.unweave_global
|
117
|
+
end
|
118
|
+
else
|
119
|
+
fail("Unrecognised command \"#{command}\"")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def build_options
|
124
|
+
opts = OptionParser.new
|
125
|
+
opts.banner = "Usage: syrup [options] start [name] | stop [name] | run [<path> | name] | activate [name] <path> |\n" +
|
126
|
+
" set <prop>=<value> | unset <prop> | clear | weave <fabric> | unweave"
|
127
|
+
opts.separator ""
|
128
|
+
opts.separator "Ruby options:"
|
129
|
+
opts.on('-d', '--debug', 'set debugging flags (set $DEBUG to true)') { $DEBUG = true }
|
130
|
+
opts.on("-I", "--include PATH", "specify $LOAD_PATH (may be used more than once)") { |path|
|
131
|
+
$LOAD_PATH.unshift(*path.split(":"))
|
132
|
+
}
|
133
|
+
opts.on("-r", "--require LIBRARY", "require the library, before executing your script") { |library|
|
134
|
+
require library
|
135
|
+
}
|
136
|
+
|
137
|
+
opts.separator ""
|
138
|
+
opts.separator "Syrup options:"
|
139
|
+
opts.on('-p', '--path PATH', "Sets the base path for this syrup configuration. Defaults to ~/.syrup. See also the --local option to set this") { |value|
|
140
|
+
@options.directory = value
|
141
|
+
}
|
142
|
+
opts.on('--local', "Sets the configuration base path to ./.syrup, allowing for a local configuration") {
|
143
|
+
@options.directory = File.expand_path('./.syrup')
|
144
|
+
}
|
145
|
+
opts.on('--application NAME', "Selects the application that should be updated. Defaults to global settings.") { |value|
|
146
|
+
@options.application = value
|
147
|
+
}
|
148
|
+
opts.on('--verbose', "Places Syrup in verbose mode.") {
|
149
|
+
@options.verbose = true
|
150
|
+
}
|
151
|
+
|
152
|
+
opts.separator ""
|
153
|
+
opts.separator "Common options:"
|
154
|
+
opts.on_tail("-h", "--help", "Show this message") { puts opts; exit 0 }
|
155
|
+
opts.on_tail('-v', '--version', "Print the version and exit") { output_version; exit 0 }
|
156
|
+
|
157
|
+
opts
|
158
|
+
end
|
159
|
+
|
160
|
+
# Determines the command issued in the provided arguments
|
161
|
+
def determine_command(arguments)
|
162
|
+
command = nil
|
163
|
+
command_args = []
|
164
|
+
|
165
|
+
# Work through each argument until we reach the command. Then, consume everything
|
166
|
+
# after it as arguments to the command
|
167
|
+
arguments.each do |arg|
|
168
|
+
if command.nil?
|
169
|
+
command = arg unless arg =~ /^-/
|
170
|
+
else
|
171
|
+
command_args << arg
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
return command, command_args
|
176
|
+
end
|
177
|
+
|
178
|
+
# Outputs the version
|
179
|
+
def output_version
|
180
|
+
puts "Syrup version #{Syrup::Application.version}"
|
181
|
+
end
|
182
|
+
|
183
|
+
def fail(msg)
|
184
|
+
puts "ERROR: #{msg}"
|
185
|
+
puts @opts
|
186
|
+
exit
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Logger
|
191
|
+
def error(msg)
|
192
|
+
puts "ERROR: #{msg}"
|
193
|
+
end
|
194
|
+
def warn(msg)
|
195
|
+
puts "WARN: #{msg}"
|
196
|
+
end
|
197
|
+
def info(msg)
|
198
|
+
puts "INFO: #{msg}"
|
199
|
+
end
|
200
|
+
def debug(msg)
|
201
|
+
puts "DEBUG: #{msg}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Module Singleton methods
|
206
|
+
class << self
|
207
|
+
def application
|
208
|
+
@application ||= Syrup::Application.new
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'syrup/config_store'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
describe Syrup::ConfigStore do
|
5
|
+
before :each do
|
6
|
+
# Ensure that we have a clear test data directory
|
7
|
+
@path = File.join(File.dirname(__FILE__), '..', '..', 'test-data')
|
8
|
+
FileUtils.rm_r @path if File.directory? @path
|
9
|
+
FileUtils.mkdir_p @path
|
10
|
+
|
11
|
+
# Create the store
|
12
|
+
@store = Syrup::ConfigStore.new @path
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return an empty list of applications when unconfigured" do
|
16
|
+
@store.applications.length.should == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should create an application when requested" do
|
20
|
+
@store.create_application 'myapp'
|
21
|
+
@store.applications['myapp'].should_not be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return the created application when one is created" do
|
25
|
+
@store.create_application('myapp').name.should == 'myapp'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return no properties on a new config" do
|
29
|
+
@store.create_application 'myapp'
|
30
|
+
@store.applications['myapp'].properties.length.should == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should allow properties to be stored in a global context" do
|
34
|
+
@store.create_application 'myconfapp'
|
35
|
+
@store.properties['a']= 'b'
|
36
|
+
@store.properties['a'].should == 'b'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should allow properties to be cleared for the global content" do
|
40
|
+
@store.create_application 'myconfapp'
|
41
|
+
@store.properties['a']= 'b'
|
42
|
+
@store.properties.clear
|
43
|
+
@store.properties.length.should == 0
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should allow properties to be deleted for the global context" do
|
47
|
+
@store.create_application 'myconfapp'
|
48
|
+
@store.properties['a']= 'b'
|
49
|
+
@store.properties['b']= 'c'
|
50
|
+
@store.properties.delete 'b'
|
51
|
+
@store.properties.length.should == 1
|
52
|
+
@store.properties['a'].should == 'b'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should allow all properties to be deleted for the global context" do
|
56
|
+
@store.create_application 'myconfapp'
|
57
|
+
@store.properties['a']= 'b'
|
58
|
+
@store.properties['b']= 'c'
|
59
|
+
@store.properties.delete 'a'
|
60
|
+
@store.properties.delete 'b'
|
61
|
+
@store.properties.length.should == 0
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should return no properties on a new application" do
|
65
|
+
@store.create_application 'myapp'
|
66
|
+
@store.applications['myapp'].properties.length.should == 0
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should allow properties to be stored for an application" do
|
70
|
+
@store.create_application 'myconfapp'
|
71
|
+
@store.applications['myconfapp'].properties['a']= 'b'
|
72
|
+
@store.applications['myconfapp'].properties['a'].should == 'b'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should allow properties to be cleared for an application" do
|
76
|
+
@store.create_application 'myconfapp'
|
77
|
+
@store.applications['myconfapp'].properties['a']= 'b'
|
78
|
+
@store.applications['myconfapp'].properties.clear
|
79
|
+
@store.applications['myconfapp'].properties.length.should == 0
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should allow properties to be deleted for an application" do
|
83
|
+
@store.create_application 'myconfapp'
|
84
|
+
@store.applications['myconfapp'].properties['a']= 'b'
|
85
|
+
@store.applications['myconfapp'].properties['b']= 'c'
|
86
|
+
@store.applications['myconfapp'].properties.delete 'b'
|
87
|
+
@store.applications['myconfapp'].properties.length.should == 1
|
88
|
+
@store.applications['myconfapp'].properties['a'].should == 'b'
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should allow all properties to be deleted for an application" do
|
92
|
+
@store.create_application 'myconfapp'
|
93
|
+
@store.applications['myconfapp'].properties['a']= 'b'
|
94
|
+
@store.applications['myconfapp'].properties['b']= 'c'
|
95
|
+
@store.applications['myconfapp'].properties.delete 'a'
|
96
|
+
@store.applications['myconfapp'].properties.delete 'b'
|
97
|
+
@store.applications['myconfapp'].properties.length.should == 0
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should return a nil fabric by default" do
|
101
|
+
@store.fabric.should be_nil
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should store the fabric location when set" do
|
105
|
+
@store.fabric = '../myfabric'
|
106
|
+
Syrup::ConfigStore.new(@path).fabric.should == File.expand_path('../myfabric')
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should allow the fabric to be cleared" do
|
110
|
+
@store.fabric = '../myfabric'
|
111
|
+
@store.fabric = nil
|
112
|
+
Syrup::ConfigStore.new(@path).fabric.should be_nil
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should return a nil fabric by default for applications" do
|
116
|
+
@store.create_application('myfabapp')
|
117
|
+
@store.applications['myfabapp'].fabric.should be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should store the fabric location when set for applications" do
|
121
|
+
@store.create_application('myfabapp')
|
122
|
+
@store.applications['myfabapp'].fabric = '../myappfabric'
|
123
|
+
Syrup::ConfigStore.new(@path).applications['myfabapp'].fabric.should == File.expand_path('../myappfabric')
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should allow the fabric to be cleared for application" do
|
127
|
+
@store.create_application('myfabapp')
|
128
|
+
@store.applications['myfabapp'].fabric = '../myappfabric'
|
129
|
+
@store.applications['myfabapp'].fabric = nil
|
130
|
+
Syrup::ConfigStore.new(@path).applications['myfabapp'].fabric.should be_nil
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should return a nil application path by default for applications" do
|
134
|
+
@store.create_application('myfabapp')
|
135
|
+
@store.applications['myfabapp'].app.should be_nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should store the application path when set for applications" do
|
139
|
+
@store.create_application('myfabapp')
|
140
|
+
@store.applications['myfabapp'].app = '../myapp.rb'
|
141
|
+
Syrup::ConfigStore.new(@path).applications['myfabapp'].app.should == File.expand_path('../myapp.rb')
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should allow the application path to be cleared for application" do
|
145
|
+
@store.create_application('myfabapp')
|
146
|
+
@store.applications['myfabapp'].app = '../myapp.rb'
|
147
|
+
@store.applications['myfabapp'].app = nil
|
148
|
+
Syrup::ConfigStore.new(@path).applications['myfabapp'].app.should be_nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should return an empty list of start parameters for a new application" do
|
152
|
+
@store.create_application('mynpapp')
|
153
|
+
@store.applications['mynpapp'].start_parameters.should == []
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should allow start parameters to be stored for an application" do
|
157
|
+
@store.create_application('mypapp')
|
158
|
+
@store.applications['mypapp'].start_parameters = ['param1', 'param 2']
|
159
|
+
|
160
|
+
Syrup::ConfigStore.new(@path).applications['mypapp'].start_parameters.should == ['param1', 'param 2']
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should allow start parameters to be cleared for an application" do
|
164
|
+
@store.create_application('mycapp')
|
165
|
+
@store.applications['mycapp'].start_parameters = ['param1', 'param 2']
|
166
|
+
@store.applications['mycapp'].start_parameters = []
|
167
|
+
|
168
|
+
Syrup::ConfigStore.new(@path).applications['mycapp'].start_parameters.should == []
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should return a nil pid for a new application" do
|
172
|
+
@store.create_application('mypidapp')
|
173
|
+
@store.applications['mypidapp'].pid.should be_nil
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should allow start parameters to be stored for an application" do
|
177
|
+
@store.create_application('mypidapp')
|
178
|
+
@store.applications['mypidapp'].pid = 1234
|
179
|
+
|
180
|
+
Syrup::ConfigStore.new(@path).applications['mypidapp'].pid.should == 1234
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should allow the pid to be cleared for an application" do
|
184
|
+
@store.create_application('mypidapp')
|
185
|
+
@store.applications['mypidapp'].pid = 1234
|
186
|
+
@store.applications['mypidapp'].pid = nil
|
187
|
+
|
188
|
+
Syrup::ConfigStore.new(@path).applications['mypidapp'].pid.should be_nil
|
189
|
+
end
|
190
|
+
end
|