vuderacha-syrup 0.1.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.
- 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
|