├── .gitignore
├── LICENSE-2.0.txt
├── README.rdoc
├── Rakefile
├── assembly.xml
├── bin
├── galaxy
├── galaxy-agent
└── galaxy-console
├── build
├── rpm
│ └── galaxy.spec
├── start-scripts
│ ├── galaxy-agent
│ └── galaxy-console
└── sun
│ ├── checkinstall
│ ├── pkginfo
│ ├── postinstall
│ ├── postremove
│ ├── preinstall
│ ├── preremove
│ ├── prototype
│ └── root
│ └── var
│ └── svc
│ └── manifest
│ └── application
│ └── management
│ ├── galaxy-agent.xml
│ └── galaxy-console.xml
├── lib
└── galaxy
│ ├── agent.rb
│ ├── agent_remote_api.rb
│ ├── agent_utils.rb
│ ├── announcements.rb
│ ├── client.rb
│ ├── command.rb
│ ├── commands
│ ├── assign.rb
│ ├── cleanup.rb
│ ├── clear.rb
│ ├── perform.rb
│ ├── reap.rb
│ ├── restart.rb
│ ├── rollback.rb
│ ├── show.rb
│ ├── show_agent.rb
│ ├── show_console.rb
│ ├── show_core.rb
│ ├── ssh.rb
│ ├── start.rb
│ ├── stop.rb
│ ├── update.rb
│ └── update_config.rb
│ ├── config.rb
│ ├── console.rb
│ ├── controller.rb
│ ├── daemon.rb
│ ├── db.rb
│ ├── deployer.rb
│ ├── events.rb
│ ├── fetcher.rb
│ ├── filter.rb
│ ├── host.rb
│ ├── log.rb
│ ├── parallelize.rb
│ ├── properties.rb
│ ├── proxy_console.rb
│ ├── report.rb
│ ├── repository.rb
│ ├── software.rb
│ ├── starter.rb
│ ├── temp.rb
│ ├── transport.rb
│ ├── version.rb
│ └── versioning.rb
├── pom.xml
└── test
├── bad_core_package
├── bin
│ ├── control
│ ├── launcher
│ └── xndeploy
└── stuff
├── core_package
├── bin
│ ├── control
│ ├── launcher
│ └── xndeploy
└── stuff
├── helper.rb
├── performance
├── build.xml
├── lib
│ ├── commons-logging-1.1.1.jar
│ ├── httpclient-4.0.1.jar
│ ├── httpcore-4.0.1.jar
│ ├── httpmime-4.0.1.jar
│ └── one-jar-ant-task-0.96.jar
└── src
│ └── LoadTest.java
├── property_data
├── a
│ ├── b
│ │ ├── c
│ │ │ ├── d
│ │ │ │ └── test_override.properties
│ │ │ └── test_simple.properties
│ │ ├── test_override.properties
│ │ └── xncore.properties
│ ├── build.properties
│ └── test_comments_ignored.properties
└── foo-bar.properties
├── test_agent.rb
├── test_announcements.rb
├── test_client.rb
├── test_commands.rb
├── test_config.rb
├── test_console.rb
├── test_controller.rb
├── test_db.rb
├── test_deployer.rb
├── test_event.rb
├── test_fetcher.rb
├── test_filter.rb
├── test_host.rb
├── test_logger.rb
├── test_logger_collector.rb
├── test_parallelize.rb
├── test_propbuilder.rb
├── test_repository.rb
├── test_temp.rb
└── test_transport.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | pkg/
2 | *.o
3 | *.lo
4 | *.pc
5 | *.log
6 | *.status
7 | .deps/
8 | *.iml
9 | *.ipr
10 | *.iws
11 | coverage/
12 | target/
13 | .rakeTasks
14 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'fileutils'
3 | require 'tmpdir'
4 | require 'rake'
5 | require 'rake/testtask'
6 | require 'rake/clean'
7 | require 'rake/gempackagetask'
8 | require 'lib/galaxy/version'
9 | begin
10 | require 'rcov/rcovtask'
11 | $RCOV_LOADED = true
12 | rescue LoadError
13 | $RCOV_LOADED = false
14 | puts "Unable to load rcov"
15 | end
16 |
17 | THIS_FILE = File.expand_path(__FILE__)
18 | PWD = File.dirname(THIS_FILE)
19 | RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
20 |
21 | PACKAGE_NAME = 'galaxy'
22 | PACKAGE_VERSION = Galaxy::Version
23 | GEM_VERSION = PACKAGE_VERSION.split('-')[0]
24 |
25 | task :default => [:test]
26 |
27 | task :install do
28 | sitelibdir = Config::CONFIG["sitelibdir"]
29 | cd 'lib' do
30 | for file in Dir["galaxy/*.rb", "galaxy/commands/*.rb" ]
31 | d = File.join(sitelibdir, file)
32 | mkdir_p File.dirname(d)
33 | install(file, d)
34 | end
35 | end
36 |
37 | bindir = Config::CONFIG["bindir"]
38 | cd 'bin' do
39 | for file in ["galaxy", "galaxy-agent", "galaxy-console" ]
40 | d = File.join(bindir, file)
41 | mkdir_p File.dirname(d)
42 | install(file, d)
43 | end
44 | end
45 | end
46 |
47 |
48 | Rake::TestTask.new("test") do |t|
49 | t.pattern = 'test/test*.rb'
50 | t.libs << 'test'
51 | t.warning = true
52 | end
53 |
54 | if $RCOV_LOADED
55 | Rcov::RcovTask.new do |t|
56 | t.pattern = 'test/test*.rb'
57 | t.libs << 'test'
58 | t.rcov_opts = ['--exclude', 'gems/*', '--text-report']
59 | end
60 | end
61 |
62 | Rake::PackageTask.new(PACKAGE_NAME, PACKAGE_VERSION) do |p|
63 | p.tar_command = 'gtar' if RUBY_PLATFORM =~ /solaris/
64 | p.need_tar = true
65 | p.package_files.include(["lib/galaxy/**/*.rb", "bin/*"])
66 | end
67 |
68 | spec = Gem::Specification.new do |s|
69 | s.name = PACKAGE_NAME
70 | s.version = GEM_VERSION
71 | s.author = "Ning, Inc."
72 | s.email = "pierre@ning.com"
73 | s.homepage = "http://github.com/ning/galaxy"
74 | s.platform = Gem::Platform::RUBY
75 | s.summary = "Galaxy is a lightweight software deployment and management tool."
76 | s.files = FileList["lib/galaxy/**/*.rb", "bin/*"]
77 | s.executables = FileList["galaxy-agent", "galaxy-console", "galaxy"]
78 | s.require_path = "lib"
79 | s.add_dependency("fileutils", ">= 0.7")
80 | s.add_dependency("json", ">= 1.5.1")
81 | s.add_dependency("mongrel", ">= 1.1.5")
82 | s.add_dependency("rcov", ">= 0.9.9")
83 | end
84 |
85 | Rake::GemPackageTask.new(spec) do |pkg|
86 | pkg.need_zip = false
87 | pkg.tar_command = 'gtar' if RUBY_PLATFORM =~ /solaris/
88 | pkg.need_tar = true
89 | end
90 |
91 | namespace :run do
92 | desc "Run a Gonsole locally"
93 | task :gonsole do
94 | # Note that -i localhost is needed. Otherwise the DRb server will bind to the
95 | # hostname, which can be as ugly as "Pierre-Alexandre-Meyers-MacBook-Pro.local"
96 | system(RUBY, "-I", File.join(PWD, "lib"),
97 | File.join(PWD, "bin", "galaxy-console"), "--start",
98 | "-i", "localhost",
99 | "--ping-interval", "10", "-f", "-l", "STDOUT", "-L", "DEBUG", "-v")
100 | end
101 |
102 | desc "Run a Gagent locally"
103 | task :gagent do
104 | system(RUBY, "-I", File.join(PWD, "lib"),
105 | File.join(PWD, "bin", "galaxy-agent"), "--start",
106 | "-i", "localhost", "-c", "localhost",
107 | "-r", "http://localhost/config/trunk/qa",
108 | "-b", "http://localhost/binaries",
109 | "-d", "/tmp/deploy", "-x", "/tmp/extract",
110 | "--announce-interval", "10", "-f", "-l", "STDOUT", "-L", "DEBUG", "-v")
111 | end
112 | end
113 |
114 | desc "Build a Gem with the full version number"
115 | task :versioned_gem => :gem do
116 | gem_version = PACKAGE_VERSION.split('-')[0]
117 | if gem_version != PACKAGE_VERSION
118 | FileUtils.mv("pkg/#{PACKAGE_NAME}-#{gem_version}.gem", "pkg/#{PACKAGE_NAME}-#{PACKAGE_VERSION}.gem")
119 | end
120 | end
121 |
122 | namespace :package do
123 | desc "Build an RPM package"
124 | task :rpm => :versioned_gem do
125 | build_dir = "/tmp/galaxy-package"
126 | rpm_dir = "/tmp/galaxy-rpm"
127 | rpm_version = PACKAGE_VERSION
128 | rpm_version += "-final" unless rpm_version.include?('-')
129 |
130 | FileUtils.rm_rf(build_dir)
131 | FileUtils.mkdir_p(build_dir)
132 | FileUtils.rm_rf(rpm_dir)
133 | FileUtils.mkdir_p(rpm_dir)
134 |
135 | `rpmbuild --target=noarch -v --define "_builddir ." --define "_rpmdir #{rpm_dir}" -bb build/rpm/galaxy.spec` || raise("Failed to create package")
136 | # You can tweak the rpm as follow:
137 | #`rpmbuild --target=noarch -v --define "_gonsole_url gonsole.company.com" --define "_gepo_url http://gepo.company.com/config/trunk/prod" --define "_builddir ." --define "_rpmdir #{rpm_dir}" -bb build/rpm/galaxy.spec` || raise("Failed to create package")
138 |
139 | FileUtils.cp("#{rpm_dir}/noarch/#{PACKAGE_NAME}-#{rpm_version}.noarch.rpm", "pkg/#{PACKAGE_NAME}-#{rpm_version}.noarch.rpm")
140 | FileUtils.rm_rf(build_dir)
141 | FileUtils.rm_rf(rpm_dir)
142 | end
143 |
144 | desc "Build a Sun package"
145 | task :sunpkg => :versioned_gem do
146 | build_dir = "#{Dir.tmpdir}/galaxy-package"
147 | source_dir = File.dirname(__FILE__)
148 |
149 | FileUtils.rm_rf(build_dir)
150 | FileUtils.mkdir_p(build_dir)
151 | FileUtils.cp_r("#{source_dir}/build/sun/.", build_dir)
152 | FileUtils.cp("#{source_dir}/pkg/#{PACKAGE_NAME}-#{PACKAGE_VERSION}.gem", "#{build_dir}/#{PACKAGE_NAME}.gem")
153 | FileUtils.mkdir_p("#{build_dir}/root/lib/svc/method")
154 |
155 | # Expand version tokens
156 | `ruby -pi -e "gsub('\#{PACKAGE_VERSION}', '#{PACKAGE_VERSION}'); gsub('\#{GEM_VERSION}', '#{GEM_VERSION}')" #{build_dir}/*`
157 |
158 | # Build the package
159 | `cd #{build_dir} && pkgmk -r root -d .` || raise("Failed to create package")
160 | `cd #{build_dir} && pkgtrans -s . #{PACKAGE_NAME}.pkg galaxy` || raise("Failed to translate package")
161 |
162 | FileUtils.cp("#{build_dir}/#{PACKAGE_NAME}.pkg", "#{source_dir}/pkg/#{PACKAGE_NAME}-#{PACKAGE_VERSION}.pkg")
163 | FileUtils.rm_rf(build_dir)
164 | end
165 | end
166 |
--------------------------------------------------------------------------------
/assembly.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | tar.gz
4 |
5 | false
6 |
7 |
8 |
9 | lib/**
10 |
11 | /
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/bin/galaxy:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'rubygems'
4 | require 'optparse'
5 | require 'resolv'
6 | require 'timeout'
7 |
8 | require 'galaxy/client'
9 | require 'galaxy/command'
10 | require 'galaxy/config'
11 | require 'galaxy/host'
12 | require 'galaxy/version'
13 | require 'galaxy/transport'
14 | require 'galaxy/versioning'
15 |
16 | @filter = {}
17 | @options = {
18 | :console_url => ENV['GALAXY_CONSOLE'],
19 | :thread_count => 25,
20 | :versioning_policy => Galaxy::Versioning::StrictVersioningPolicy,
21 | }
22 |
23 | @opts = OptionParser.new do |opts|
24 | opts.banner = "#{$0} [options] [args]"
25 |
26 | opts.separator ""
27 | opts.separator "Options:"
28 | opts.on("-h", "--help", "Display a help message and exit") { @options[:help_requested] = true }
29 | opts.on("-c", "--console CONSOLE", "Galaxy console host (overrides GALAXY_CONSOLE)") { |arg| @options[:console_url] = arg }
30 | opts.on("-C", "--config FILE", "Configuration file (overrides GALAXY_CONFIG)") { |arg| @options[:config_file] = arg }
31 | opts.on("-p", "--parallel-count THREADS", "Maximum number of threads to use, default #{@options[:thread_count]}") { |arg| @options[:thread_count] = arg.to_i }
32 | opts.on("-r", "--relaxed-versioning", "Allow updates to the currently assigned version") { @options[:versioning_policy] = Galaxy::Versioning::RelaxedVersioningPolicy }
33 | opts.on("-V", "Display the Galaxy version number and exit") do |x|
34 | puts "Galaxy version #{Galaxy::Version}"
35 | @options[:version_requested] = true
36 | end
37 | opts.on("-y", "--yes", "Avoid confirmation prompts by automatically confirming all actions") { @options[:implicit_confirmation] = true }
38 |
39 | opts.separator ""
40 | opts.separator "Filters:"
41 |
42 | opts.on("-i", "--host HOST", "Select a specific agent by hostname") do |arg|
43 | @filter[:host] = arg
44 | end
45 |
46 | opts.on("-I", "--ip IP", "Select a specific agent by IP address") do |arg|
47 | @filter[:ip] = arg
48 | end
49 |
50 | opts.on("-m", "--machine MACHINE", "Select agents by physical machine") do |arg|
51 | @filter[:machine] = arg
52 | end
53 |
54 | opts.on("-M", "--cohabitants HOST", "Select agents that share a physical machine with the specified host") do |arg|
55 | @options[:cohabitant_host] = arg
56 | end
57 |
58 | opts.on("-s", "--set SET", "Select 'e{mpty}', 't{aken}' or 'a{ll}' hosts", [:empty, :all, :taken, :e, :a, :t]) do |arg|
59 | case arg
60 | when :all, :a then
61 | @filter[:set] = :all
62 | when :empty, :e then
63 | @filter[:set] = :empty
64 | when :taken, :t then
65 | @filter[:set] = :taken
66 | end
67 | end
68 |
69 | opts.on("-S", "--state STATE", "Select 'r{unning}' or 's{topped}' hosts", [:running, :stopped, :r, :s]) do |arg|
70 | case arg
71 | when :running, :r then
72 | @filter[:state] = 'running'
73 | when :stopped, :s then
74 | @filter[:state] = 'stopped'
75 | end
76 | end
77 |
78 | opts.on("-A", "--agent-state STATE", "Select 'online' or 'offline' agents", [:online, :offline]) do |arg|
79 | case arg
80 | when :online then
81 | @filter[:agent_state] = 'online'
82 | when :offline then
83 | @filter[:agent_state] = 'offline'
84 | end
85 | end
86 |
87 | opts.on("-e", "--env ENV", "Select agents in the given environment") { |arg| @filter[:env] = arg }
88 | opts.on("-t", "--type TYPE", "Select agents with a given software type") { |arg| @filter[:type] = arg }
89 | opts.on("-v", "--version VERSION", "Select agents with a given software version") { |arg| @filter[:version] = arg }
90 |
91 | opts.separator ""
92 | opts.separator "Notes:"
93 | opts.separator " - Filters are evaluated as: set | host | (env & version & type)"
94 | opts.separator " - The HOST, MACHINE, and TYPE arguments are regular expressions (not globs)"
95 | opts.separator " - The default filter selects all hosts"
96 |
97 | begin
98 | @original_args = ARGV.join(" ")
99 | @args = opts.parse! ARGV
100 | @filter[:command] = @original_args
101 | rescue Exception => msg
102 | puts opts
103 | puts msg
104 | exit 1
105 | end
106 | end
107 |
108 | def parse_command_line
109 | begin
110 | @options[:config_from_file] = Galaxy::Config::read_config_file(@options[:config_file])
111 | get_command
112 | abort(usage_message) if @options[:help_requested]
113 | validate_options
114 | rescue CommandLineError => e
115 | puts usage_message if @command_class
116 | $stderr.puts "Error: #{e}" unless e.message.empty?
117 | exit(1)
118 | end
119 | end
120 |
121 | def get_command
122 | command_name = @args.shift
123 | unless @options[:help_requested]
124 | raise CommandLineError.new("Missing command") if command_name.nil?
125 | end
126 |
127 | unless command_name.nil?
128 | @command_class = Galaxy::Commands[command_name]
129 | if @command_class.nil?
130 | raise CommandLineError.new("Unrecognized command: #{command_name}")
131 | end
132 | @command = @command_class.new(@args, @options)
133 | end
134 |
135 | # If a host is removed from dns, it should then be possible to reap it from the gonsole, without
136 | # having to restart the gonsole. Don't bail out if the DNS does not exist anymore.
137 | # See GAL-290.
138 | begin
139 | @filter[:host] = canonical_hostname(@filter[:host]) if @filter[:host]
140 | rescue Exception => e
141 | raise CommandLineError.new("DNS error: #{e}") unless command_name == "reap"
142 | @filter[:host] = @filter[:host]
143 | end
144 | begin
145 | @filter[:machine] = canonical_hostname(@filter[:machine]) if @filter[:machine]
146 | rescue Exception => e
147 | raise CommandLineError.new("DNS error: #{e}") unless command_name == "reap"
148 | @filter[:machine] = @filter[:machine]
149 | end
150 | begin
151 | @options[:cohabitant_host] = canonical_hostname(@options[:cohabitant_host]) if @options[:cohabitant_host]
152 | rescue Exception => e
153 | raise CommandLineError.new("DNS error: #{e}") unless command_name == "reap"
154 | @options[:cohabitant_host] = @options[:cohabitant_host]
155 | end
156 | end
157 |
158 | def validate_options
159 | console_url = @options[:console_url] || @options[:config_from_file]['galaxy.client.console']
160 | if console_url.nil?
161 | raise CommandLineError.new("Cannot determine console host; consider passing -c or setting GALAXY_CONSOLE")
162 | end
163 | @options[:console_url] = normalize_console_url(console_url)
164 | @options[:console] = Galaxy::Transport.locate(@options[:console_url])
165 |
166 | if @options[:cohabitant_host]
167 | begin
168 | agents = @options[:console].agents({:host => @options[:cohabitant_host], :command => @original_args})
169 | if agents.length != 1
170 | raise "Found #{agents.length} agents matching #{@options[:cohabitant_host]}"
171 | end
172 | @filter[:machine] = agents[0].machine
173 | rescue Exception => e
174 | raise "Unable to determine machine for host #{@options[:cohabitant_host]}: #{e}"
175 | end
176 | end
177 | end
178 |
179 | def usage_message
180 | @opts.separator ""
181 |
182 | if @command_class.nil?
183 | @opts.separator "Commands:"
184 | Galaxy::Commands.each do |command_name|
185 | @opts.separator " #{command_name}"
186 | end
187 | else
188 | @opts.separator "Usage for '#{@command_class.name}':"
189 |
190 | help = @command_class.help
191 | indent = help.scan(/^\s+/).first
192 |
193 | help.split("\n").each do |line|
194 | @opts.separator line.gsub(/^#{indent}/, " ")
195 | end
196 | end
197 | @opts.to_s
198 | end
199 |
200 | def get_agents
201 | @agents = @command.select_agents(@filter)
202 | rescue Exception => e
203 | abort("Error: #{e}")
204 | end
205 |
206 | def validate_agents
207 | # If command is show-console, it's okay not to have found any agent
208 | if @agents.length == 0 and @command.class.name != "show-console"
209 | abort("No agents matching the provided filter(s) were available for #{@command.class.name}")
210 | elsif @agents.length > 1 and (@command.changes_agent_state or @command.changes_console_state) and not@options[:implicit_confirmation]
211 | abort unless prompt_and_wait_for_user_confirmation("#{@agents.length} agents will be affected; continue? (y/n) ")
212 | end
213 | locate_agent_proxies # AgentProxy should provide this instead of having to instantiate it here
214 | end
215 |
216 | def locate_agent_proxies
217 | @agents.each { |agent| agent.proxy = Galaxy::Transport.locate(agent.url) if agent.url }
218 | end
219 |
220 | def run_command
221 | user = Galaxy::HostUtils::shell_user || 'unknown'
222 | message = "#{user} ran: galaxy " + @original_args
223 | stdout, stderr = @command.execute(@agents)
224 | puts stdout unless stdout.nil?
225 | $stderr.puts stderr unless stderr.nil?
226 | end
227 |
228 | exit(0) if @options[:version_requested]
229 | parse_command_line
230 | get_agents
231 | validate_agents
232 | run_command
233 |
--------------------------------------------------------------------------------
/bin/galaxy-agent:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | tried = false
4 | begin
5 | require 'galaxy/agent'
6 | require 'galaxy/daemon'
7 | require 'galaxy/config'
8 | require 'galaxy/version'
9 | rescue LoadError
10 | tried = true
11 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
12 | tried ? raise : retry
13 | end
14 | require 'optparse'
15 | require 'ostruct'
16 |
17 | action = "help"
18 | command_line_options = OpenStruct.new
19 | opts = OptionParser.new do |opts|
20 | opts.banner = "Usage: #{$0} [options]"
21 |
22 | opts.separator " Commands, use just one of these"
23 | opts.on("-s", "--start", "Start the agent") { action = "start" }
24 | opts.on("-k", "--stop", "Stop the agent") { action = "stop" }
25 |
26 | opts.separator " Options for Start"
27 | opts.on("-C", "--config FILE", "Configuration file (overrides GALAXY_CONFIG)") do |arg|
28 | command_line_options.config_file = arg
29 | end
30 | opts.on("-i", "--host HOST[:PORT]", "Hostname this agent manages (default localhost)") do |host|
31 | command_line_options.host = host
32 | end
33 | opts.on("-m", "--machine MACHINE", "Physical machine where the agent lives (overrides -M)") do |machine|
34 | command_line_options.machine = machine
35 | end
36 | opts.on("-M", "--machine-file FILE", "Filename containing the physical machine name") do |machine_file|
37 | command_line_options.machine_file = machine_file
38 | end
39 | opts.on("-c", "--console ['http://'|'druby://']HOST[:PORT]", "Hostname where the console is listening") do |console|
40 | command_line_options.console = console
41 | end
42 | opts.on("-r", "--repository URL", "Base URL for the repository") do |repo|
43 | command_line_options.repository = repo
44 | end
45 | opts.on("-b", "--binaries URL", "Base URL for the binary archive") do |bin|
46 | command_line_options.binaries = bin
47 | end
48 | opts.on("-d", "--deploy-to DIR", "Directory where to make deployments") do |path|
49 | command_line_options.deploy_dir = path
50 | end
51 | opts.on("-x", "--data DIR", "Directory for the agent's database") do |path|
52 | command_line_options.data_dir = path
53 | end
54 | opts.on("-f", "--fore", "--foreground", "Run agent in the foreground") do
55 | command_line_options.foreground = true
56 | end
57 | opts.on("-a", "--announce-interval INTERVAL", "How frequently (in seconds) the agent should announce") do |interval|
58 | command_line_options.announce_interval = interval
59 | end
60 |
61 | opts.separator " General Options"
62 | opts.on_tail("-l", "--log LOG", "STDOUT | STDERR | SYSLOG | /path/to/file.log") do |log|
63 | command_line_options.log = log
64 | end
65 | opts.on_tail("-L", "--log-level LEVEL", "DEBUG | INFO | WARN | ERROR. Default=INFO") do |level|
66 | command_line_options.log_level = level
67 | end
68 | opts.on_tail("-u", "--user USER", "User to run as") do |arg|
69 | command_line_options.user = arg
70 | end
71 | opts.on("-z", "--event_listener URL", "Which listener to use") do |event_listener|
72 | command_line_options.event_listener = event_listener
73 | end
74 | opts.on("-H", "--http-user USER", "HTTP User for authentication.") do |http_user|
75 | command_line_options.http_user = http_user
76 | end
77 | opts.on("-P", "--http-password PASSWORD", "HTTP Password for authentication.") do |http_password|
78 | command_line_options.http_password = http_password
79 | end
80 | opts.on_tail("-g", "--agent-log FILE", "File agent should rediect stdout and stderr to") do |log|
81 | command_line_options.agent_log = log
82 | end
83 |
84 | opts.on_tail("-t", "--test", "Test, displays as -v without doing anything") do
85 | command_line_options.verbose = true
86 | command_line_options.test = true
87 | end
88 | opts.on_tail("-v", "--verbose", "Verbose output") { command_line_options.verbose = true }
89 | opts.on_tail("-V", "--version", "Print the galaxy version and exit") { action = "version" }
90 | opts.on_tail("-h", "--help", "Show this help") { action = "help" }
91 |
92 |
93 | begin
94 | opts.parse! ARGV
95 | rescue Exception => msg
96 | puts opts
97 | puts msg
98 | exit 1
99 | end
100 | end
101 |
102 | case action
103 | when "help"
104 | puts opts
105 |
106 | when "version"
107 | puts "Galaxy version #{Galaxy::Version}"
108 |
109 | when "start"
110 | config = Galaxy::AgentConfigurator.new(command_line_options).configure
111 | exit if command_line_options.test
112 | if command_line_options.foreground
113 | agent = Galaxy::Agent.start config
114 | agent.join
115 | else
116 | Galaxy::Daemon.start('galaxy-agent', config[:pid_file], config[:user]) do
117 | agent = Galaxy::Agent.start config
118 | agent.join
119 | end
120 | end
121 |
122 | when "stop"
123 | config = Galaxy::AgentConfigurator.new(command_line_options).configure
124 | begin
125 | Galaxy::Daemon.kill_daemon(config[:pid_file])
126 | rescue Exception => e
127 | abort("Error: #{e}")
128 | end
129 |
130 | end
131 |
--------------------------------------------------------------------------------
/bin/galaxy-console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'optparse'
3 | require 'ostruct'
4 |
5 | tried = false
6 | begin
7 | require 'galaxy/console'
8 | require 'galaxy/proxy_console'
9 | require 'galaxy/config'
10 | require 'galaxy/daemon'
11 | require 'galaxy/host'
12 | require 'galaxy/version'
13 | rescue LoadError
14 | tried = true
15 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
16 | tried ? raise : retry
17 | end
18 |
19 | action = "help"
20 | command_line_options = OpenStruct.new
21 | opts = OptionParser.new do |opts|
22 | opts.banner = "Usage: #{$0} [options]"
23 |
24 | opts.separator " Commands, use just one of these"
25 | opts.on("-s", "--start", "Start the console") { action = "start" }
26 | opts.on("-P", "--start-proxy", "Start the proxy console") { action = "start-proxy" }
27 | opts.on("-k", "--stop", "Stop the console") { action = "stop" }
28 |
29 | opts.separator " Options for Start"
30 | opts.on("-C", "--config FILE", "Configuration file (overrides GALAXY_CONFIG)") do |arg|
31 | command_line_options.config_file = arg
32 | end
33 | opts.on("-i", "--host HOST", "Hostname this console runs on") do |host|
34 | command_line_options.host = host
35 | end
36 | opts.on("-a", "--announcement-url HOST[:PORT]", "Port for Http post announcements") do |ann_host|
37 | command_line_options.announcement_url = ann_host
38 | end
39 | opts.on("-p", "--ping-interval INTERVAL", "How many seconds an agent can be silent before being marked dead") do |interval|
40 | command_line_options.ping_interval = interval
41 | end
42 | opts.on("-f", "--fore", "--foreground", "Run console in the foreground") do
43 | command_line_options.foreground = true
44 | end
45 | opts.on("-Q", "--console-proxied-url URL", "Gonsole to proxy") do |host|
46 | command_line_options.console_proxyied_url = host
47 | end
48 |
49 |
50 | opts.separator " General Options"
51 | opts.on_tail("-l", "--log LOG", "STDOUT | STDERR | SYSLOG | /path/to/file.log") do |log|
52 | command_line_options.log = log
53 | end
54 | opts.on_tail("-L", "--log-level LEVEL", "DEBUG | INFO | WARN | ERROR. Default=INFO") do |level|
55 | command_line_options.log_level = level
56 | end
57 | opts.on_tail("-g", "--console-log FILE", "File agent should rediect stdout and stderr to") do |log|
58 | command_line_options.agent_log = log
59 | end
60 | opts.on_tail("-u", "--user USER", "User to run as") do |arg|
61 | command_line_options.user = arg
62 | end
63 | opts.on("-z", "--event_listener URL", "Which listener to use") do |event_listener|
64 | command_line_options.event_listener = event_listener
65 | end
66 | opts.on_tail("-t", "--test", "Test, displays as -v without doing anything") do
67 | command_line_options.verbose = true
68 | command_line_options.test = true
69 | end
70 | opts.on_tail("-v", "--verbose", "Verbose output") { command_line_options.verbose = true }
71 | opts.on_tail("-V", "--version", "Print the galaxy version and exit") { action = "version" }
72 | opts.on_tail("-h", "--help", "Show this help") { action = "help" }
73 |
74 | begin
75 | opts.parse! ARGV
76 | rescue Exception => msg
77 | puts opts
78 | puts msg
79 | exit 1
80 | end
81 | end
82 |
83 | case action
84 | when "help"
85 | puts opts
86 | exit
87 |
88 | when "version"
89 | puts "Galaxy version #{Galaxy::Version}"
90 |
91 | when "start"
92 | config = Galaxy::ConsoleConfigurator.new(command_line_options).configure
93 | exit if command_line_options.test
94 | if command_line_options.foreground
95 | console = Galaxy::Console.start config
96 | console.join
97 | else
98 | Galaxy::Daemon.start('galaxy-console', config[:pid_file], config[:user]) do
99 | console = Galaxy::Console.start(config)
100 | console.join
101 | end
102 | end
103 | when "start-proxy"
104 | config = Galaxy::ConsoleConfigurator.new(command_line_options).configure
105 | exit if command_line_options.test
106 | if command_line_options.foreground
107 | console = Galaxy::ProxyConsole.start config
108 | console.join
109 | else
110 | Galaxy::Daemon.start('galaxy-proxy-console', config[:pid_file], config[:user]) do
111 | console = Galaxy::ProxyConsole.start(config)
112 | console.join
113 | end
114 | end
115 | when "stop"
116 | config = Galaxy::ConsoleConfigurator.new(command_line_options).configure
117 | begin
118 | Galaxy::Daemon.kill_daemon(config[:pid_file])
119 | rescue Exception => e
120 | abort("Error: #{e}")
121 | end
122 |
123 | end
124 |
--------------------------------------------------------------------------------
/build/rpm/galaxy.spec:
--------------------------------------------------------------------------------
1 | %define gemversion %(ruby -rlib/galaxy/version -e 'puts Galaxy::Version.split("-", 1)[0]' 2>/dev/null)
2 | %define gemname galaxy
3 |
4 | Name: %{gemname}
5 | Summary: Software deployment tool
6 | Version: %{gemversion}
7 | Release: %(ruby -rlib/galaxy/version -e 'puts Galaxy::Version.split("-", 2)[1] || "final"' 2>/dev/null)
8 | License: Apache License, version 2.0
9 | Group: Development/Tools/Other
10 | URL: http://github.com/ning/galaxy
11 | BuildArch: noarch
12 | Requires: ruby
13 | BuildRoot: /tmp/galaxy-package
14 | Provides: rubygem(%{gemname}) = %{gemversion}
15 |
16 | %define gem %(ruby -rlib/galaxy/version -e 'puts "galaxy-#{Galaxy::Version}.gem"')
17 |
18 | # Use rpmbuild --define "_gonsole_url gonsole.prod.company.com" to customize
19 | # galaxy.{client,agent}.console in galaxy.conf
20 | %{?!_gonsole_url: %define _gonsole_url GONSOLE_URL}
21 |
22 | # Use rpmbuild --define "_gepo_url http://gepo.company.com/config/trunk/prod" to customize
23 | # galaxy.agent.config-root in galaxy.conf
24 | %{?!_gepo_url: %define _gepo_url GEPO_URL}
25 |
26 | # Use rpmbuild --define "_gepobin_url http://gepo.company.com/binaries" to customize
27 | # galaxy.agent.config-root in galaxy.conf
28 | %{?!_gepobin_url: %define _gepobin_url GEPOBIN_URL}
29 |
30 | %description
31 | Galaxy is a lightweight software deployment and management tool used to manage the Java cores and Apache httpd instances that make up the Ning platform.
32 |
33 | %prep
34 |
35 | %build
36 |
37 | %install
38 | mkdir -p %{buildroot}/var/cache/gem
39 | cp pkg/%{gem} %{buildroot}/var/cache/gem
40 | mkdir -p %{buildroot}/etc/rc.d/init.d
41 | cp -r build/start-scripts/* %{buildroot}/etc/rc.d/init.d
42 | find %{buildroot}/etc/rc.d/init.d -type f | xargs chmod a+x
43 |
44 | %clean
45 | rm -rf %{buildroot}
46 |
47 | %files
48 | %defattr(-, root, root, -)
49 | /var/cache/gem/*
50 | /etc/rc.d/init.d/*
51 |
52 | %post
53 | # Stop and disable the agent (/etc/galaxy.conf required)
54 | [ -f /etc/galaxy.conf ] && service galaxy-agent stop
55 | chkconfig galaxy-agent off
56 |
57 | # Stop and disable the gonsole (/etc/galaxy.conf required)
58 | [ -f /etc/galaxy.conf ] && service galaxy-console stop
59 | chkconfig galaxy-console off
60 |
61 | # Don't kill... On Linux jails, this will affect other zones.
62 | # We have to trust the pid...
63 | ## Kill rogue Galaxy processes
64 | #killed_some=
65 | #for pid in `ps -ef | grep galaxy | grep -v grep | grep -v rpm | awk '{print $2}'`
66 | #do
67 | # kill $pid
68 | # killed_some=true
69 | #done
70 | #[ ! -z $killed_some ] && sleep 5
71 | #for pid in `ps -ef | grep galaxy | grep -v grep | grep -v rpm | awk '{print $2}'`
72 | #do
73 | # kill -9 $pid
74 | #done
75 |
76 | # Install the Galaxy gem
77 | gem install /var/cache/gem/%{gem}
78 |
79 | # Write Galaxy configuration
80 | # We assume that it is an agent by default. You'll need to update this template
81 | # on the gonsole
82 | if [ ! -f "/etc/galaxy.conf" ]; then
83 | cat < /etc/galaxy.conf
84 | #
85 | # Galaxy client properties
86 | #
87 | galaxy.client.console: %_gonsole_url
88 |
89 | ##
90 | ## Galaxy console properties
91 | ##
92 | #galaxy.console.log: SYSLOG
93 | #galaxy.console.log-level: INFO
94 | #galaxy.console.ping-interval: 90
95 | #galaxy.console.user: xncore
96 | #galaxy.console.pid-file: /home/xncore/galaxy-console.pid
97 |
98 | #
99 | # Galaxy agent properties
100 | #
101 | galaxy.agent.console: %_gonsole_url
102 | galaxy.agent.config-root: %_gepo_url
103 | galaxy.agent.binaries-root: %_gepobin_url
104 | galaxy.agent.deploy-dir: /home/xncore/deploy
105 | galaxy.agent.data-dir: /home/xncore/data
106 | galaxy.agent.log: SYSLOG
107 | galaxy.agent.log-level: INFO
108 | galaxy.agent.announce-interval: 60
109 | galaxy.agent.user: xncore
110 | galaxy.agent.pid-file: /home/xncore/galaxy-agent.pid
111 | EOF
112 |
113 | # Create /etc/rc.d files for Galaxy agent
114 | /sbin/chkconfig --add galaxy-agent
115 |
116 | # Turn on the agent (/etc/galaxy.conf required)
117 | [ -f /etc/galaxy.conf ] && /sbin/service galaxy-agent start
118 | /sbin/chkconfig galaxy-agent on
119 |
120 | # The console is not turned on by default
121 | # /sbin/chkconfig --add galaxy-console
122 |
123 | %preun
124 | # Stop Galaxy services
125 | [ -f /etc/galaxy.conf ] && service galaxy-agent stop
126 | [ -f /etc/galaxy.conf ] && service galaxy-console stop
127 |
128 | # Remove Galaxy services
129 | /sbin/chkconfig --del galaxy-agent
130 | /sbin/chkconfig --del galaxy-console
131 |
132 | %postun
133 | # Uninstall the Galaxy gem
134 | gem uninstall -v=%{version} %{name}
135 | # rpm -Uvh will install first and uninstall the old version afterwards
136 | #/bin/rm -f /etc/galaxy.conf
137 |
--------------------------------------------------------------------------------
/build/start-scripts/galaxy-agent:
--------------------------------------------------------------------------------
1 | #!/bin/bash -l
2 | # chkconfig: 2345 80 90
3 | # description: Activates/Deactivates Galaxy agent
4 |
5 | GALAXY_CONFIG="/etc/galaxy.conf"
6 | GALAXY_USER=`ruby -ryaml -e "puts YAML.load(File.open('$GALAXY_CONFIG'))['galaxy.agent.user'] || ''"`
7 |
8 | if [ -z "$GALAXY_USER" ]
9 | then
10 | echo "Error: Unable to determine galaxy agent user. Please set the galaxy.agent.user property in $GALAXY_CONFIG."
11 | exit 2
12 | fi
13 |
14 | case "$1" in
15 | 'start')
16 | logger -p daemon.notice "Starting galaxy-agent"
17 | su - $GALAXY_USER -c 'galaxy-agent --start'
18 | ;;
19 | 'stop')
20 | logger -p daemon.notice "Stopping galaxy-agent"
21 | su - $GALAXY_USER -c 'galaxy-agent --stop'
22 | ;;
23 | 'restart')
24 | logger -p daemon.notice "Restarting galaxy-agent"
25 | su - $GALAXY_USER -c 'galaxy-agent --stop'
26 | su - $GALAXY_USER -c 'galaxy-agent --start'
27 | ;;
28 | 'status')
29 | galaxy-agent --status
30 | ;;
31 | *)
32 | echo "Usage: $0 "
33 | exit 1
34 | ;;
35 | esac
36 |
37 |
--------------------------------------------------------------------------------
/build/start-scripts/galaxy-console:
--------------------------------------------------------------------------------
1 | #!/bin/bash -l
2 | # chkconfig: 2345 80 90
3 | # description: Activates/Deactivates Galaxy console
4 |
5 | GALAXY_CONFIG="/etc/galaxy.conf"
6 | GALAXY_USER=`ruby -ryaml -e "puts YAML.load(File.open('$GALAXY_CONFIG'))['galaxy.console.user'] || ''"`
7 |
8 | if [ -z "$GALAXY_USER" ]
9 | then
10 | echo "Error: Unable to determine galaxy console user. Please set the galaxy.console.user property in $GALAXY_CONFIG."
11 | exit 2
12 | fi
13 |
14 | case "$1" in
15 | 'start')
16 | logger -p daemon.notice "Starting galaxy-console"
17 | su - $GALAXY_USER -c 'galaxy-console --start'
18 | ;;
19 | 'stop')
20 | logger -p daemon.notice "Stopping galaxy-console"
21 | su - $GALAXY_USER -c 'galaxy-console --stop'
22 | ;;
23 | 'restart')
24 | logger -p daemon.notice "Restarting galaxy-console"
25 | su - $GALAXY_USER -c 'galaxy-console --stop'
26 | su - $GALAXY_USER -c 'galaxy-console --start'
27 | ;;
28 | 'status')
29 | galaxy-console --status
30 | ;;
31 | *)
32 | echo "Usage: $0 "
33 | exit 1
34 | ;;
35 | esac
36 |
--------------------------------------------------------------------------------
/build/sun/checkinstall:
--------------------------------------------------------------------------------
1 | #!/bin/bash -l
2 | #
3 | # Determines whether Galaxy can be installed on this host
4 | #
5 |
6 | #
7 | # Reinvoke under bash to configure PATH
8 | #
9 | if [ -z "$BASH" ]
10 | then
11 | exec /bin/bash -l $0 $*
12 | fi
13 |
14 | # This script is run by nobody, who may not have ruby on the PATH
15 | PATH="/usr/local/bin:$PATH"
16 |
17 | RUBY_VERSION=`ruby --version`
18 |
19 | if [ -z "$RUBY_VERSION" ]
20 | then
21 | echo "Error: This package requires Ruby"
22 | exit 1
23 | fi
24 |
25 | GEM_VERSION=`gem --version`
26 |
27 | if [ -z "$GEM_VERSION" ]
28 | then
29 | echo "Error: This package requires RubyGems"
30 | exit 1
31 | fi
32 |
--------------------------------------------------------------------------------
/build/sun/pkginfo:
--------------------------------------------------------------------------------
1 | PKG="galaxy"
2 | NAME="Galaxy :: Lightweight Code Deployment"
3 | DESC="Galaxy is a lightweight software deployment and management tool."
4 | SUNW_PKG_ALLZONES=true
5 | BASEDIR=/
6 | VERSION="3.0.0"
7 | ARCH="all"
8 | CLASSES="none"
9 | CATEGORY="application"
10 | VENDOR="Ning, Inc."
11 | EMAIL="pierre@ning.com"
12 |
--------------------------------------------------------------------------------
/build/sun/postinstall:
--------------------------------------------------------------------------------
1 | #!/bin/bash -l
2 | #
3 | # Installs the bundled Galaxy gem and imports the Galaxy SMF services
4 | #
5 |
6 | #
7 | # Reinvoke under bash to configure PATH
8 | #
9 | if [ -z "$BASH" ]
10 | then
11 | exec /bin/bash -l $0 $*
12 | fi
13 |
14 | PACKAGE="galaxy"
15 | PACKAGE_VERSION="#{PACKAGE_VERSION}"
16 | GEM_VERSION="#{GEM_VERSION}"
17 |
18 | # The filesystem containing the gem directory may be mounted read-only on
19 | # zones with shared filesystems. Do not install the gem in these cases,
20 | # but warn the user so they know that the gem was not installed.
21 | gemdir=`ruby -rubygems -e 'puts Gem::dir'`
22 |
23 | # We can't just use '[ -w $gemdir ]' here because -w only checks the mode
24 | # bits on the file and cannot determine whether the underlying filesystem
25 | # is mounted read-only.
26 | WRITABLE_TEST="$gemdir/galaxy-write-test"
27 | touch $WRITABLE_TEST 2>/dev/null
28 |
29 | if [ $? -eq 0 ]
30 | then
31 | rm $WRITABLE_TEST
32 | gem install /var/sadm/pkg/galaxy/install/$PACKAGE.gem
33 | if [ $? -ne 0 ]
34 | then
35 | echo "Error: Unable to install the $PACKAGE-$GEM_VERSION gem"
36 | exit 1
37 | fi
38 | else
39 | echo "WARNING: Skipping Gem installation for unwritable directory $gemdir (FINE ON ZONES)"
40 | fi
41 |
42 | # Configure galaxy.conf
43 | file=/etc/galaxy.conf
44 | CONSOLE="gonsole.company.com"
45 | GEPO="gepo.company.com"
46 | ENVIRONMENT="prod"
47 | if [ ! -e "$file" ]; then
48 | cat < $file
49 | #
50 | # Galaxy client properties
51 | #
52 | galaxy.client.console: $CONSOLE
53 |
54 | #
55 | # Galaxy agent properties
56 | #
57 | galaxy.agent.console: $CONSOLE
58 | galaxy.agent.config-root: http://$GEPO/config/trunk/$ENVIRONMENT
59 | galaxy.agent.binaries-root: http://$GEPO/binaries
60 | galaxy.agent.deploy-dir: /home/xncore/deploy
61 | galaxy.agent.data-dir: /home/xncore/data
62 | galaxy.agent.log: SYSLOG
63 | galaxy.agent.log-level: INFO
64 | galaxy.agent.announce-interval: 60
65 | galaxy.agent.user: xncore
66 | galaxy.agent.pid-file: /home/xncore/galaxy-agent.pid
67 | EOF
68 | fi
69 |
70 | svccfg import /var/svc/manifest/application/management/galaxy-agent.xml
71 | svccfg import /var/svc/manifest/application/management/galaxy-console.xml
72 |
73 | svccfg -s galaxy-agent setprop start/user = astring: xncore
74 | svccfg -s galaxy-agent setprop start/group = astring: xncore
75 |
76 | # Start Galaxy agent
77 | svcadm enable "galaxy-agent"
78 |
--------------------------------------------------------------------------------
/build/sun/postremove:
--------------------------------------------------------------------------------
1 | #!/bin/bash -l
2 | #
3 | # Removes the previously installed Galaxy gem
4 | #
5 |
6 | #
7 | # Reinvoke under bash to configure PATH
8 | #
9 | if [ -z "$BASH" ]
10 | then
11 | exec /bin/bash -l $0 $*
12 | fi
13 |
14 | PACKAGE="galaxy"
15 | PACKAGE_VERSION="#{PACKAGE_VERSION}"
16 | GEM_VERSION="#{GEM_VERSION}"
17 |
18 | # The filesystem containing the gem directory may be mounted read-only on
19 | # zones with shared filesystems. Do not install the gem in these cases,
20 | # but warn the user so they know that the gem was not installed.
21 | gemdir=`ruby -rubygems -e 'puts Gem::dir'`
22 |
23 | # We can't just use '[ -w $gemdir ]' here because -w only checks the mode
24 | # bits on the file and cannot determine whether the underlying filesystem
25 | # is mounted read-only.
26 | WRITABLE_TEST="$gemdir/galaxy-write-test"
27 | touch $WRITABLE_TEST 2>/dev/null
28 |
29 | if [ $? -eq 0 ]
30 | then
31 | rm $WRITABLE_TEST
32 | gem uninstall -v=$GEM_VERSION $PACKAGE
33 |
34 | if [ $? -ne 0 ]
35 | then
36 | echo "Error: Unable to remove $PACKAGE-$GEM_VERSION gem"
37 | exit 1
38 | fi
39 | rm -f /etc/galaxy.conf
40 | else
41 | echo "WARNING: Skipping Gem removal for unwritable directory $gemdir"
42 | fi
43 |
--------------------------------------------------------------------------------
/build/sun/preinstall:
--------------------------------------------------------------------------------
1 | #!/bin/bash -l
2 | #
3 | # Prepare for installation of Galaxy Agent.
4 | #
5 | # Solaris boxes don't run gonsoles.
6 | #
7 |
8 | #
9 | # Reinvoke under bash to configure PATH
10 | #
11 | if [ -z "$BASH" ]
12 | then
13 | exec /bin/bash -l $0 $*
14 | fi
15 |
16 | PACKAGE="galaxy"
17 | PACKAGE_VERSION="#{PACKAGE_VERSION}"
18 | GEM_VERSION="#{GEM_VERSION}"
19 |
20 | # Stop and disable Galaxy Agent
21 |
22 | ZONENAME=`zonename`
23 | if [ $ZONENAME = global ]
24 | then
25 | for zone in `zoneadm list -cv | awk '$3 == "running" && $2 != "global" { print $2 }'` # Non-global zones
26 | do
27 | zlogin $zone svcadm disable "galaxy-agent"
28 | done
29 | else
30 | svcadm disable "galaxy-agent" # Global zone
31 | fi
32 |
33 | # Kill rogue Galaxy Agent processes
34 | killed_some=
35 | for pid in `ps -ef | grep galaxy-agent | grep -v grep | awk '{print $2}'`
36 | do
37 | kill $pid
38 | killed_some=true
39 | done
40 | [ ! -z $killed_some ] && sleep 5
41 | for pid in `ps -ef | grep galaxy-agent | grep -v grep | awk '{print $2}'`
42 | do
43 | kill -9 $pid
44 | done
45 |
--------------------------------------------------------------------------------
/build/sun/preremove:
--------------------------------------------------------------------------------
1 | #!/bin/bash -l
2 | #
3 | # Disables and removes the previously installed Galaxy SMF services
4 | #
5 |
6 | #
7 | # Reinvoke under bash to configure PATH
8 | #
9 | if [ -z "$BASH" ]
10 | then
11 | exec /bin/bash -l $0 $*
12 | fi
13 |
14 | svcadm disable galaxy-agent
15 |
16 | # Give the daemons time to stop
17 | sleep 3
18 |
19 | svccfg delete -f galaxy-agent
20 |
21 | # Don't fail if the daemon wasn't enabled for some reason
22 | exit 0
23 |
--------------------------------------------------------------------------------
/build/sun/prototype:
--------------------------------------------------------------------------------
1 | i pkginfo
2 | i checkinstall
3 | i preinstall
4 | i postinstall
5 | i preremove
6 | i postremove
7 | i galaxy.gem
8 | f none var/svc/manifest/application/management/galaxy-agent.xml 0644 root root
9 | f none var/svc/manifest/application/management/galaxy-console.xml 0644 root root
10 |
--------------------------------------------------------------------------------
/build/sun/root/var/svc/manifest/application/management/galaxy-agent.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
32 |
33 |
34 |
38 |
39 |
40 |
41 |
45 |
46 |
47 |
48 |
49 |
50 |
55 |
56 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | galaxy-agent daemon
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/build/sun/root/var/svc/manifest/application/management/galaxy-console.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
32 |
33 |
34 |
38 |
39 |
40 |
41 |
45 |
46 |
47 |
48 |
49 |
50 |
55 |
56 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | galaxy-console daemon
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/lib/galaxy/agent.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 | require 'logger'
3 | require 'ostruct'
4 | require 'resolv'
5 | require 'socket'
6 | require 'stringio'
7 | require 'yaml'
8 |
9 | require 'galaxy/agent_remote_api'
10 | require 'galaxy/config'
11 | require 'galaxy/controller'
12 | require 'galaxy/db'
13 | require 'galaxy/deployer'
14 | require 'galaxy/events'
15 | require 'galaxy/fetcher'
16 | require 'galaxy/log'
17 | require 'galaxy/properties'
18 | require 'galaxy/repository'
19 | require 'galaxy/software'
20 | require 'galaxy/starter'
21 | require 'galaxy/transport'
22 | require 'galaxy/version'
23 | require 'galaxy/versioning'
24 |
25 | module Galaxy
26 | class Agent
27 | attr_reader :host, :machine, :config, :locked, :logger, :gonsole_url
28 | attr_accessor :starter, :fetcher, :deployer, :db
29 |
30 | include Galaxy::AgentRemoteApi
31 |
32 | def initialize host, url, machine, announcements_url, repository_base, deploy_dir,
33 | data_dir, binaries_base, http_user, http_password, log, log_level, announce_interval, event_listener
34 | @drb_url = url
35 | @host = host
36 | @machine = machine
37 | @http_user = http_user
38 | @http_password = http_password
39 | @ip = Resolv.getaddress(@host)
40 |
41 | # Setup the logger and the event dispatcher (HDFS) if needed
42 | @logger = Galaxy::Log::Glogger.new log, event_listener, announcements_url, @ip
43 | @logger.log.level = log_level
44 |
45 | @lock = OpenStruct.new(:owner => nil, :count => 0, :mutex => Mutex.new)
46 |
47 | # set up announcements
48 | @gonsole_url = announcements_url
49 | @announcer = Galaxy::Transport.locate announcements_url, @logger
50 |
51 | # Setup event listener
52 | @event_dispatcher = Galaxy::GalaxyEventSender.new(event_listener, @gonsole_url, @ip, @logger)
53 |
54 | @announce_interval = announce_interval
55 | @prop_builder = Galaxy::Properties::Builder.new repository_base, @http_user, @http_password, @logger
56 | @repository = Galaxy::Repository.new repository_base, @logger
57 | @deployer = Galaxy::Deployer.new deploy_dir, @logger
58 | @fetcher = Galaxy::Fetcher.new binaries_base, @http_user, @http_password, @logger
59 | @starter = Galaxy::Starter.new @logger
60 | @db = Galaxy::DB.new data_dir
61 | @repository_base = repository_base
62 | @binaries_base = binaries_base
63 |
64 | if RUBY_PLATFORM =~ /\w+-(\D+)/
65 | @os = $1
66 | @logger.debug "Detected OS: #{@os}"
67 | end
68 |
69 | @logger.debug "Detected machine: #{@machine}"
70 |
71 | @config = read_config current_deployment_number
72 |
73 | Galaxy::Transport.publish url, self
74 | announce
75 | sync_state!
76 |
77 | @thread = Thread.start do
78 | loop do
79 | sleep @announce_interval
80 | announce
81 | end
82 | end
83 | end
84 |
85 | def lock
86 | @lock.mutex.synchronize do
87 | raise "Agent is locked performing another operation" unless @lock.owner.nil? || @lock.owner == Thread.current
88 |
89 | @lock.owner = Thread.current if @lock.owner.nil?
90 |
91 | @logger.debug "Locking from #{caller[2]}" if @lock.count == 0
92 | @lock.count += 1
93 | end
94 | end
95 |
96 | def unlock
97 | @lock.mutex.synchronize do
98 | raise "Lock not owned by current thread" unless @lock.owner.nil? || @lock.owner == Thread.current
99 | @lock.count -= 1
100 | @lock.owner = nil if @lock.count == 0
101 |
102 | @logger.debug "Unlocking from #{caller[2]}" if @lock.count == 0
103 | end
104 | end
105 |
106 | def status
107 | OpenStruct.new(
108 | :host => @host,
109 | :ip => @ip,
110 | :url => @drb_url,
111 | :os => @os,
112 | :machine => @machine,
113 | :core_type => config.core_type,
114 | :config_path => config.config_path,
115 | :build => config.build,
116 | :status => @starter.status(config.core_base),
117 | :last_start_time => config.last_start_time,
118 | :agent_status => 'online',
119 | :galaxy_version => Galaxy::Version
120 | )
121 | end
122 |
123 | def announce
124 | begin
125 | res = @announcer.announce status
126 | @event_dispatcher.dispatch_announce_success_event status
127 | return res
128 | rescue Exception => e
129 | error_reason = "Unable to communicate with console, #{e.message}"
130 | @logger.warn "Unable to communicate with console, #{e.message}"
131 | @logger.warn e
132 | @event_dispatcher.dispatch_announce_error_event error_reason
133 | end
134 | end
135 |
136 | def read_config deployment_number
137 | config = nil
138 | deployment_number = deployment_number.to_s
139 | data = @db[deployment_number]
140 | unless data.nil?
141 | begin
142 | config = YAML.load data
143 | unless config.is_a? OpenStruct
144 | config = nil
145 | raise "Expecting serialized OpenStruct"
146 | end
147 | rescue Exception => e
148 | @logger.warn "Error reading deployment descriptor: #{@db.file_for(deployment_number)}: #{e}"
149 | end
150 | end
151 | config ||= OpenStruct.new
152 | # Ensure autostart=true for pre-2.5 deployments
153 | if config.auto_start.nil?
154 | config.auto_start = true
155 | end
156 | config
157 | end
158 |
159 | def write_config deployment_number, config
160 | deployment_number = deployment_number.to_s
161 | @db[deployment_number] = YAML.dump config
162 | end
163 |
164 | def current_deployment_number
165 | @db['deployment'] ||= "0"
166 | @db['deployment'].to_i
167 | end
168 |
169 | def current_deployment_number= deployment_number
170 | deployment_number = deployment_number.to_s
171 | @db['deployment'] = deployment_number
172 | @config = read_config deployment_number
173 | end
174 |
175 | # private
176 | def sync_state!
177 | lock
178 |
179 | begin
180 | if @config
181 | # Get the status from the core
182 | status = @starter.status @config.core_base
183 | @config.state = status
184 | write_config current_deployment_number, @config
185 | end
186 | ensure
187 | unlock
188 | end
189 | end
190 |
191 | # Stop the agent
192 | def shutdown
193 | @starter.stop! config.core_base if config
194 | @thread.kill
195 | Galaxy::Transport.unpublish @drb_url
196 | end
197 |
198 | # Wait for the agent to finish
199 | def join
200 | @thread.join
201 | end
202 |
203 | # args: host => IP/Name to uniquely identify this agent
204 | # console => hostname of the console
205 | # repository => base of url to repository
206 | # binaries => base of url=l to binary repository
207 | # deploy_dir => /path/to/deployment
208 | # data_dir => /path/to/agent/data/storage
209 | # log => /path/to/log || STDOUT || STDERR || SYSLOG
210 | # url => url to listen on
211 | # event_listener => url of the event listener
212 | def Agent.start args
213 | host_url = args[:host] || "localhost"
214 | host_url = "druby://#{host_url}" unless host_url.match("^http://") || host_url.match("^druby://") # defaults to drb
215 | host_url = "#{host_url}:4441" unless host_url.match ":[0-9]+$"
216 |
217 | # default console to http/4442 unless specified
218 | console_url = args[:console] || "localhost"
219 | console_url = "http://" + console_url unless console_url.match("^http://") || console_url.match("^druby://")
220 | console_url += ":4442" unless console_url.match ":[0-9]+$"
221 |
222 | # need host as simple name without protocol or port
223 | host = args[:host] || "localhost"
224 | host = host.sub(/^http:\/\//, "")
225 | host = host.sub(/^druby:\/\//, "")
226 | host = host.sub(/:[0-9]+$/, "")
227 |
228 | if args[:machine]
229 | machine = args[:machine]
230 | else
231 | machine_file = args[:machine_file] || Galaxy::Config::DEFAULT_MACHINE_FILE
232 | if File.exists? machine_file
233 | File.open machine_file, "r" do |f|
234 | machine = f.read.chomp
235 | end
236 | else
237 | machine = Socket.gethostname
238 | end
239 | end
240 |
241 | agent = Agent.new host,
242 | host_url,
243 | machine,
244 | console_url,
245 | args[:repository] || "/tmp/galaxy-agent-properties",
246 | args[:deploy_dir] || "/tmp/galaxy-agent-deploy",
247 | args[:data_dir] || "/tmp/galaxy-agent-data",
248 | args[:binaries] || "http://localhost:8000",
249 | args[:http_user],
250 | args[:http_password],
251 | args[:log] || "STDOUT",
252 | args[:log_level] || Logger::INFO,
253 | args[:announce_interval] || 60,
254 | args[:event_listener]
255 |
256 | agent
257 | end
258 |
259 | private :initialize, :sync_state!, :config
260 | end
261 |
262 | end
263 |
--------------------------------------------------------------------------------
/lib/galaxy/agent_utils.rb:
--------------------------------------------------------------------------------
1 | require 'timeout'
2 |
3 | module Galaxy
4 | module AgentUtils
5 | def ping_agent agent
6 | Timeout::timeout(5) do
7 | agent.proxy.status
8 | end
9 | end
10 |
11 | module_function :ping_agent
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/galaxy/announcements.rb:
--------------------------------------------------------------------------------
1 | require 'net/http'
2 | require 'uri'
3 | require 'yaml'
4 | require 'ostruct'
5 | require 'logger'
6 | require 'rubygems'
7 |
8 | begin
9 | # mongrel is installed only on the gonsole, not on agent machines
10 | require 'mongrel'
11 | $MONGREL_LOADED = true
12 | rescue LoadError
13 | $MONGREL_LOADED = false
14 | end
15 |
16 | begin
17 | # We don't install the json gem by default on our machines.
18 | # Not critical, since it is for the HTTP API that will be rolled in the future
19 | require 'json'
20 | $JSON_LOADED = true
21 | rescue LoadError
22 | $JSON_LOADED = false
23 | end
24 |
25 | module Galaxy
26 | module HTTPUtils
27 | def url_escape(string)
28 | string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
29 | '%' + $1.unpack('H2' * $1.size).join('%').upcase
30 | end.tr(' ', '+')
31 | end
32 |
33 | def url_unescape(string)
34 | string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
35 | [$1.delete('%')].pack('H*')
36 | end
37 | end
38 | end
39 |
40 | if $MONGREL_LOADED
41 | class HTTPServer
42 | def initialize(url, console, callbacks=nil, log=nil)
43 | @log = log || Logger.new(STDOUT)
44 |
45 | # Create server
46 | begin
47 | @server = Mongrel::HttpServer.new("0.0.0.0", get_port(url))
48 | rescue Exception => err
49 | msg = "HTTP server initialization error: #{err}"
50 | @log.error msg
51 | raise IOError, msg
52 | end
53 |
54 | @server.register("/", ReceiveAnnouncement.new(console), true)
55 | @server.register("/status", AnnouncementStatus.new, true)
56 |
57 | # Actually start the server
58 | @thread = Thread.new do
59 | begin
60 | @server.run.join
61 | rescue Exception => err
62 | msg = "HTTP server start error: #{err}"
63 | @log.error msg
64 | raise msg
65 | end
66 | end
67 | end
68 |
69 | # parse the port from the given url string
70 | def get_port(url)
71 | begin
72 | last = url.count(':')
73 | raise "malformed url: '#{url}'." if last==0 || last>2
74 | port = url.split(':')[last].to_i
75 | rescue Exception => err
76 | msg = "Problem parsing port for string '#{url}': error = #{err}"
77 | @log.error msg
78 | raise msg
79 | end
80 | port
81 | end
82 |
83 | def shutdown
84 | if @server
85 | @server.stop
86 | @server.graceful_shutdown
87 | @thread.join
88 | @server = @thread = nil
89 | end
90 | end
91 | end
92 |
93 |
94 | # POST handler that receives announcements and calls the callback function with the data payload
95 | class ReceiveAnnouncement < Mongrel::HttpHandler
96 | ANNOUNCEMENT_RESPONSE_TEXT = 'Announcement received.'
97 |
98 | def initialize(console)
99 | @console = console
100 | end
101 |
102 | def process(request, response)
103 | response.start(200) do |head, out|
104 | head['Context-Type'] = 'text/plain; charset=utf-8'
105 | head['Connection'] = 'close'
106 | if request.params['REQUEST_METHOD'] == 'POST'
107 | @console.process_post(YAML::load(request.body))
108 | out.write ANNOUNCEMENT_RESPONSE_TEXT
109 | elsif request.params['REQUEST_METHOD'] == 'GET'
110 | out.write @console.process_get(request.params['REQUEST_PATH'])
111 | end
112 | end
113 | end
114 | end
115 |
116 | # optional GET response for querying the server status
117 | class AnnouncementStatus < Mongrel::HttpHandler
118 | def process(request, response)
119 | if request.params['REQUEST_METHOD'] == 'GET'
120 | response.start(200) do |head, out|
121 | head['Context-Type'] = 'text/plain; charset=utf-8'
122 | head['Connection'] = 'close'
123 | time = Time.now
124 | body = ""
125 | body += "Announcement Status
";
126 | body += time.strftime("%Y%m%d-%H:%M:%S") + sprintf(".%06d", time.usec)
127 | body += ""
128 | out.write body
129 | end
130 | end
131 | end
132 | end
133 | end
134 | end
135 |
136 | # HTTP client library.
137 | # Used by the galaxy agent to send announcements to the server.
138 | # Used by the command line client to query the gonsole over HTTP.
139 | class HTTPAnnouncementSender
140 | include Galaxy::HTTPUtils
141 |
142 | def initialize(url, log = nil)
143 | # eg: 'http://encomium.company.com:4440'
144 | @uri = URI.parse(url)
145 | @log = log
146 | end
147 |
148 | # Announce an agent to a gonsole
149 | # agent is an OpenStruct defining the state of the agent.
150 | def announce(agent)
151 | begin
152 | # POST
153 | Net::HTTP.start(@uri.host, @uri.port) do |http|
154 | headers = {'Content-Type' => 'text/plain; charset=utf-8', 'Connection' => 'close'}
155 | put_data = agent.to_yaml
156 | start_time = Time.now
157 | response = http.send_request('POST', @uri.request_uri, put_data, headers)
158 | @log.debug "Announcement send response time for #{agent.host} = #{Time.now-start_time}" if @log
159 | #puts "Response = #{response.code} #{response.message}: #{response.body}"
160 | response.body
161 | end
162 | rescue Exception => e
163 | @log.warn "Client side error: #{e}" if @log
164 | end
165 | end
166 |
167 | # Retrieve a list of agents matching a giving filter
168 | # args is a hash filter (cf Galaxy::Filter).
169 | def agents(args)
170 | # Convert filter string ({:set=>:all}) to URI string (/set=all)
171 | # XXX Built-in method to do that?
172 | filter = ""
173 | args.each do |key, value|
174 | filter += url_escape(key.to_s) + "=" + url_escape(value.to_s) + "&"
175 | end
176 | filter.chomp!("&")
177 |
178 | begin
179 | Net::HTTP.start(@uri.host, @uri.port) do |http|
180 | headers = {'Content-Type' => 'text/plain; charset=utf-8', 'Connection' => 'close'}
181 | start_time = Time.now
182 | response = http.send_request('GET', @uri.request_uri + filter, headers)
183 | @log.debug "Announcement send response time for #{agent.host} = #{Time.now-start_time}" if @log
184 | return JSON.parse(response.body).collect { |x| OpenStruct.new(x) }
185 | end
186 | rescue Exception => e
187 | # If the json gem is not loaded, we will log the issue here.
188 | @log.warn "Client side error: #{e}" if @log
189 | return []
190 | end
191 | end
192 |
193 | # :nodoc:
194 | # Compatibility with the DRb galaxy client.
195 | # XXX Should go away (overhead).
196 | def log(* args)
197 | nil
198 | end
199 | end
200 |
201 |
202 | ################################################################################################
203 | #
204 | # sample MAIN
205 | #
206 |
207 | # example callback for action upon receiving an announcement
208 | def on_announcement(ann)
209 | puts "...received announcement: #{ann.inspect}"
210 | end
211 |
212 | # Initialize and POST to server
213 | if $0 == __FILE__ then
214 | # start server
215 | url = 'http://encomium.company.com:4440'
216 | Galaxy::HTTPAnnouncementReceiver.new(url, lambda { |a| on_announcement(a) })
217 | announcer = HTTPAnnouncementSender.new(url)
218 |
219 | # periodically, send stuff to it
220 | loop do
221 | begin
222 |
223 | announcer.announce(OpenStruct.new(:foo=>"bar", :rand => rand(100), :item => "eggs"))
224 |
225 | puts "server running..."
226 | sleep 15
227 | rescue Exception => err
228 | STDERR.puts "* #{err}"
229 | exit(1)
230 | end
231 | end
232 | end
233 |
--------------------------------------------------------------------------------
/lib/galaxy/client.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'etc'
3 | require 'resolv'
4 |
5 | class CommandLineError < Exception;
6 | end
7 |
8 | def prompt_and_wait_for_user_confirmation prompt
9 | confirmed = false
10 | loop do
11 | $stderr.print prompt
12 | $stderr.flush
13 | case $stdin.gets.chomp.downcase
14 | when "y"
15 | confirmed = true
16 | break
17 | when "n"
18 | break
19 | else
20 | $stderr.puts "Please enter 'y' or 'n'"
21 | end
22 | end
23 | confirmed
24 | end
25 |
26 | # Expand the supplied console_url (which may just consist of hostname) to a full URL, assuming DRb as the default transport
27 | def normalize_console_url console_url
28 | console_url = "druby://#{console_url}" unless console_url.match(/^\w+:\/\//)
29 | console_url ="#{console_url}:4440" unless console_url.match(/:\d+$/)
30 | console_url
31 | end
32 |
33 | # Expand short hostnames to their fully-qualified names
34 | #
35 | # This implementation depends on the client and agents sharing a common naming service (hosts files, NIS, DNS, etc).
36 | def canonical_hostname hostname
37 | Resolv.getname(Resolv.getaddress(hostname))
38 | end
39 |
--------------------------------------------------------------------------------
/lib/galaxy/command.rb:
--------------------------------------------------------------------------------
1 | require 'galaxy/agent_utils'
2 | require 'galaxy/parallelize'
3 | require 'galaxy/report'
4 |
5 | module Galaxy
6 | module Commands
7 | @@commands = {}
8 |
9 | def self.register_command command_name, command_class
10 | @@commands[command_name] = command_class
11 | end
12 |
13 | def self.[] command_name
14 | @@commands[command_name]
15 | end
16 |
17 | def self.each
18 | @@commands.keys.sort.each { |command| yield command }
19 | end
20 |
21 | class Command
22 | class << self
23 | attr_reader :name
24 | end
25 |
26 | attr_writer :report_class, :report, :error_report
27 |
28 | def self.register_command name
29 | @name = name
30 | Galaxy::Commands.register_command name, self
31 | end
32 |
33 | def self.changes_agent_state
34 | define_method("changes_agent_state") do
35 | true
36 | end
37 | end
38 |
39 | def self.changes_console_state
40 | define_method("changes_console_state") do
41 | true
42 | end
43 | end
44 |
45 | def initialize args = [], options = {}
46 | @args = args
47 | @options = options
48 | @options[:thread_count] ||= 1
49 | end
50 |
51 | def changes_agent_state
52 | false
53 | end
54 |
55 | def changes_console_state
56 | false
57 | end
58 |
59 | def select_agents filter
60 | normalized_filter = normalize_filter(filter)
61 | @options[:console].agents(normalized_filter)
62 | end
63 |
64 | def normalize_filter filter
65 | filter = default_filter if filter.empty?
66 | filter
67 | end
68 |
69 | def default_filter
70 | {:set => :all}
71 | end
72 |
73 | def execute agents
74 | report.start
75 | error_report.start
76 | agents.parallelize(@options[:thread_count]) do |agent|
77 | begin
78 | unless agent.agent_status == 'online'
79 | raise "Agent is not online"
80 | end
81 | Galaxy::AgentUtils::ping_agent(agent)
82 | result = execute_for_agent(agent)
83 | report.record_result result
84 | rescue TimeoutError
85 | error_report.record_result "Error: Timed out communicating with agent #{agent.host}"
86 | rescue Exception => e
87 | error_report.record_result "Error: #{agent.host}: #{e}"
88 | end
89 | end
90 | return report.finish, error_report.finish
91 | end
92 |
93 | def report
94 | @report ||= report_class.new
95 | end
96 |
97 | def report_class
98 | @report_class ||= Galaxy::Client::SoftwareDeploymentReport
99 | end
100 |
101 | def error_report
102 | @error_report ||= Galaxy::Client::Report.new
103 | end
104 | end
105 | end
106 | end
107 |
108 | # load and register all commands
109 | Dir.entries("#{File.join(File.dirname(__FILE__))}/commands").each do |entry|
110 | if entry =~ /\.rb$/
111 | require "galaxy/commands/#{File.basename(entry, '.rb')}"
112 | end
113 | end
114 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/assign.rb:
--------------------------------------------------------------------------------
1 | require 'galaxy/report'
2 |
3 | module Galaxy
4 | module Commands
5 | class AssignCommand < Command
6 | register_command "assign"
7 | changes_agent_state
8 |
9 | def initialize args, options
10 | super
11 |
12 | env, version, type = * args
13 |
14 | raise CommandLineError.new(" is missing") unless env
15 | raise CommandLineError.new(" is missing") unless version
16 | raise CommandLineError.new(" is missing") unless type
17 |
18 | @config_path = "/#{env}/#{version}/#{type}"
19 | @versioning_policy = options[:versioning_policy]
20 | end
21 |
22 | def default_filter
23 | {:set => :empty}
24 | end
25 |
26 | def execute_for_agent agent
27 | agent.proxy.become!(@config_path, @versioning_policy)
28 | end
29 |
30 | def self.help
31 | return <<-HELP
32 | #{name}
33 |
34 | Deploy software to the selected hosts
35 |
36 | Parameters:
37 | env The environment
38 | version The software version
39 | type The software type
40 |
41 | These three parameters together define the configuration path (relative to the repository base):
42 |
43 | ///
44 | HELP
45 | end
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/cleanup.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class CleanupCommand < Command
4 | register_command "cleanup"
5 |
6 | def execute_for_agent agent
7 | agent.proxy.cleanup!
8 | end
9 |
10 | def self.help
11 | return <<-HELP
12 | #{name}
13 |
14 | Remove all deployments up to the current and last one, for rollback.
15 | HELP
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/clear.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class ClearCommand < Command
4 | register_command "clear"
5 | changes_agent_state
6 |
7 | def normalize_filter filter
8 | filter = super
9 | filter[:set] = :taken if filter[:set] == :all
10 | filter
11 | end
12 |
13 | def execute_for_agent agent
14 | agent.proxy.clear!
15 | end
16 |
17 | def self.help
18 | return <<-HELP
19 | #{name}
20 |
21 | Stop and clear the active software deployment on the selected hosts
22 | HELP
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/perform.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class PerformCommand < Command
4 | register_command "perform"
5 | changes_agent_state
6 |
7 | def initialize args, options
8 | super
9 |
10 | @command = args.shift
11 | raise CommandLineError.new(" is missing") unless @command
12 | @args = args
13 | end
14 |
15 | def normalize_filter filter
16 | filter = super
17 | filter[:set] = :taken if filter[:set] == :all
18 | filter
19 | end
20 |
21 | def execute_for_agent agent
22 | agent.proxy.perform! @command, @args.join(' ')
23 | end
24 |
25 | def report_class
26 | Galaxy::Client::CommandOutputReport
27 | end
28 |
29 | def self.help
30 | return <<-HELP
31 | #{name}
32 |
33 | galaxy perform [args]
34 |
35 | Launch the control script (bin/control) with the indicated command on the selected hosts, optionally passing the provided arguments
36 | HELP
37 | end
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/reap.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class ReapCommand < Command
4 | register_command "reap"
5 | changes_console_state
6 |
7 | def execute agents
8 | report.start
9 | agents.sort_by { |agent| agent.host }.each do |agent|
10 | reaped = @options[:console].reap(agent.host)
11 | report.record_result("#{agent.host} - reap #{reaped.nil? ? 'failed' : 'succeeded'}")
12 | end
13 | [report.finish, nil]
14 | end
15 |
16 | def report_class
17 | Galaxy::Client::Report
18 | end
19 |
20 | def self.help
21 | return <<-HELP
22 | #{name}
23 |
24 | Delete stale announcements (from the console) for the selected hosts, without affecting agents
25 | HELP
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/restart.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class RestartCommand < Command
4 | register_command "restart"
5 | changes_agent_state
6 |
7 | def normalize_filter filter
8 | filter = super
9 | filter[:set] = :taken if filter[:set] == :all
10 | filter
11 | end
12 |
13 | def execute_for_agent agent
14 | agent.proxy.restart!
15 | end
16 |
17 | def self.help
18 | return <<-HELP
19 | #{name}
20 |
21 | Restart the deployed software on the selected hosts
22 | HELP
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/rollback.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class RollbackCommand < Command
4 | register_command "rollback"
5 | changes_agent_state
6 |
7 | def normalize_filter filter
8 | filter = super
9 | filter[:set] = :taken if filter[:set] == :all
10 | filter
11 | end
12 |
13 | def execute_for_agent agent
14 | agent.proxy.rollback!
15 | end
16 |
17 | def self.help
18 | return <<-HELP
19 | #{name}
20 |
21 | Stop and rollback software to the previously deployed version
22 | HELP
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/show.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class ShowCommand < Command
4 | register_command "show"
5 |
6 | def execute agents
7 | report.start
8 | agents.sort_by { |agent| agent.host }.each do |agent|
9 | report.record_result agent
10 | end
11 | report.finish
12 | end
13 |
14 | def self.help
15 | return <<-HELP
16 | #{name}
17 |
18 | Show software deployments on the selected hosts
19 |
20 | Examples:
21 |
22 | - Show all hosts:
23 | galaxy show
24 |
25 | - Show unassigned hosts:
26 | galaxy -s empty show
27 |
28 | - Show assigned hosts:
29 | galaxy -s taken show
30 |
31 | - Show a specific host:
32 | galaxy -i foo.bar.com show
33 |
34 | - Show all widgets:
35 | galaxy -t widget show
36 | HELP
37 | end
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/show_agent.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class ShowAgentCommand < Command
4 | register_command "show-agent"
5 |
6 | def execute agents
7 | report.start
8 | agents.sort_by { |agent| agent.host }.each do |agent|
9 | report.record_result agent
10 | end
11 | report.finish
12 | end
13 |
14 | def report_class
15 | Galaxy::Client::AgentStatusReport
16 | end
17 |
18 | def self.help
19 | return <<-HELP
20 | #{name}
21 |
22 | Show metadata about the selected Galaxy agents
23 | HELP
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/show_console.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class ShowConsoleCommand < Command
4 | register_command "show-console"
5 |
6 | def execute agents
7 | report.start
8 | report.record_result @options[:console]
9 | report.finish
10 | end
11 |
12 | def report_class
13 | Galaxy::Client::ConsoleStatusReport
14 | end
15 |
16 | def self.help
17 | return <<-HELP
18 | #{name}
19 |
20 | Show metadata about the selected Galaxy console
21 | HELP
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/show_core.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class ShowCoreCommand < Command
4 | register_command "show-core"
5 |
6 | def execute agents
7 | report.start
8 | agents.sort_by { |agent| agent.host }.each do |agent|
9 | report.record_result agent
10 | end
11 | report.finish
12 | end
13 |
14 | def report_class
15 | Galaxy::Client::CoreStatusReport
16 | end
17 |
18 | def self.help
19 | return <<-HELP
20 | #{name}
21 |
22 | Show core status (last start time, ...) on the selected hosts
23 | See "galaxy show -h" for help and examples on flags usage
24 | HELP
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/ssh.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class SSHCommand < Command
4 | register_command "ssh"
5 |
6 | def execute agents
7 | agent = agents.first
8 | command = ENV['GALAXY_SSH_COMMAND'] || "ssh"
9 | Kernel.system "#{command} #{agent.host}" if agent
10 | end
11 |
12 | def self.help
13 | return <<-HELP
14 | #{name}
15 |
16 | Connect via ssh to the first host matching the selection criteria
17 |
18 | The GALAXY_SSH_COMMAND environment variable can be set to specify options for ssh.
19 |
20 | For example, this instructs galaxy to login as user 'foo':
21 |
22 | export GALAXY_SSH_COMMAND="ssh -l foo"
23 | HELP
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/start.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class StartCommand < Command
4 | register_command "start"
5 | changes_agent_state
6 |
7 | def normalize_filter filter
8 | filter = super
9 | filter[:set] = :taken if filter[:set] == :all
10 | filter
11 | end
12 |
13 | def execute_for_agent agent
14 | agent.proxy.start!
15 | end
16 |
17 | def self.help
18 | return <<-HELP
19 | #{name}
20 |
21 | Start the deployed software on the selected hosts
22 | HELP
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/stop.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class StopCommand < Command
4 | register_command "stop"
5 | changes_agent_state
6 |
7 | def normalize_filter filter
8 | filter = super
9 | filter[:set] = :taken if filter[:set] == :all
10 | filter
11 | end
12 |
13 | def execute_for_agent agent
14 | agent.proxy.stop!
15 | end
16 |
17 | def self.help
18 | return <<-HELP
19 | #{name}
20 |
21 | Stop the deployed software on the selected hosts
22 | HELP
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/update.rb:
--------------------------------------------------------------------------------
1 | require 'galaxy/software'
2 |
3 | module Galaxy
4 | module Commands
5 | class UpdateCommand < Command
6 | register_command "update"
7 | changes_agent_state
8 |
9 | def initialize args, options
10 | super
11 | @requested_version = args.first
12 | raise CommandLineError.new("Must specify version") unless @requested_version
13 | @versioning_policy = options[:versioning_policy]
14 | end
15 |
16 | def normalize_filter filter
17 | filter = super
18 | filter[:set] = :taken if filter[:set] == :all
19 | filter
20 | end
21 |
22 | def execute_for_agent agent
23 | if agent.config_path.nil? or agent.config_path.empty?
24 | raise "Cannot update unassigned agent"
25 | end
26 | current_config = Galaxy::SoftwareConfiguration.new_from_config_path(agent.config_path) # TODO - this should already be tracked
27 | requested_config = current_config.dup
28 | requested_config.version = @requested_version
29 | agent.proxy.become!(requested_config.config_path, @versioning_policy)
30 | end
31 |
32 | def self.help
33 | return <<-HELP
34 | #{name}
35 |
36 | Stop and update the software on the selected hosts to the specified version
37 | HELP
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/galaxy/commands/update_config.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Commands
3 | class UpdateConfigCommand < Command
4 | register_command "update-config"
5 | changes_agent_state
6 |
7 | def initialize args, options
8 | super
9 |
10 | @requested_version = args.first
11 | raise CommandLineError.new("Must specify version") unless @requested_version
12 |
13 | @versioning_policy = options[:versioning_policy]
14 | end
15 |
16 | def normalize_filter filter
17 | filter = super
18 | filter[:set] = :taken if filter[:set] == :all
19 | filter
20 | end
21 |
22 | def execute_for_agent agent
23 | if agent.config_path.nil? or agent.config_path.empty?
24 | raise "Cannot update configuration of unassigned agent"
25 | end
26 | current_config = Galaxy::SoftwareConfiguration.new_from_config_path(agent.config_path) # TODO - this should already be tracked
27 | agent.proxy.update_config!(@requested_version, @versioning_policy)
28 | end
29 |
30 | def self.help
31 | return <<-HELP
32 | #{name}
33 |
34 | Update the software configuration on the selected hosts to the specified version
35 |
36 | This does NOT redeploy or restart the software. If a restart is desired to activate the new configuration, it must be done separately.
37 | HELP
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/galaxy/console.rb:
--------------------------------------------------------------------------------
1 | require 'ostruct'
2 | require 'logger'
3 | require 'rubygems'
4 | begin
5 | # We don't install the json gem by default on our machines.
6 | # Not critical, since it is for the HTTP API that will be rolled in the future
7 | require 'json'
8 | $JSON_LOADED = true
9 | rescue LoadError
10 | $JSON_LOADED = false
11 | end
12 | require 'resolv'
13 |
14 | require 'galaxy/events'
15 | require 'galaxy/filter'
16 | require 'galaxy/log'
17 | require 'galaxy/transport'
18 | require 'galaxy/announcements'
19 |
20 | module Galaxy
21 | class Console
22 | attr_reader :db, :drb_url, :http_url, :ping_interval, :host, :env, :logger
23 |
24 | def self.locate url
25 | Galaxy::Transport.locate url
26 | end
27 |
28 | def initialize drb_url, http_url, log, log_level, ping_interval, host, env, event_listener
29 | @host = host
30 | @ip = Resolv.getaddress(@host)
31 | @env = env
32 |
33 | @drb_url = drb_url
34 | @http_url = http_url
35 |
36 | # Setup the logger and the event dispatcher (HDFS) if needed
37 | @logger = Galaxy::Log::Glogger.new(log, event_listener, @http_url, @ip)
38 | @logger.log.level = log_level
39 |
40 | @ping_interval = ping_interval
41 | @db = {}
42 | @mutex = Mutex.new
43 |
44 | # set up event listener
45 | @event_dispatcher = Galaxy::GalaxyEventSender.new(event_listener, @http_url, @ip, @logger)
46 |
47 | Thread.new do
48 | loop do
49 | begin
50 | cutoff = Time.new
51 | sleep @ping_interval
52 | ping cutoff
53 | rescue Exception => e
54 | @logger.warn "Uncaught exception in agent ping thread: #{e}"
55 | @logger.warn e.backtrace
56 | end
57 | end
58 | end
59 | end
60 |
61 | # Remote API
62 | def reap host
63 | @mutex.synchronize do
64 | @db.delete host
65 | end
66 | end
67 |
68 | # Return agents matching a filter query
69 | # Used by both HTTP and DRb API.
70 | def agents filters = {}
71 | # Log command run by the client
72 | if filters[:command]
73 | @logger.info filters[:command]
74 | filters.delete :command
75 | end
76 |
77 | filters = {:set => :all} if (filters.empty? or filters.nil?)
78 |
79 | filter = Galaxy::Filter.new filters
80 | @logger.debug "Filtering agents by #{filter}"
81 |
82 | @mutex.synchronize do
83 | @db.values.select(& filter)
84 | end
85 | end
86 |
87 | # Process announcement (ping) from agent (HTTP API)
88 | #
89 | # this function is called as a callback from http post server. We could just use the announce function as the
90 | # callback, but using this function allows us to add in different stats for post announcements.
91 | def process_post announcement
92 | announce announcement
93 | end
94 |
95 | include Galaxy::HTTPUtils
96 |
97 | # Return agents matching a filter query (HTTP API).
98 | #
99 | # Note that & in the query means actually OR.
100 | def process_get query_string
101 | # Convert env=prod&host=prod-1.company.com to {:env => "prod", :host =>
102 | # "prod-1.company.com"}
103 | filters = {}
104 | CGI::parse(query_string).each { |k, v| filters[k.to_sym] = v.first }
105 | if $JSON_LOADED
106 | return agents(filters).to_json
107 | else
108 | return agents(filters).inspect
109 | end
110 | end
111 |
112 | # Remote API
113 | def dispatch_event type, msg
114 | @event_dispatcher.send("dispatch_#{type}_event", msg)
115 | end
116 |
117 | def Console.start args
118 | host = args[:host] || "localhost"
119 | drb_url = args[:url] || "druby://" + host # DRB transport
120 | drb_url += ":4440" unless drb_url.match ":[0-9]+$"
121 |
122 | http_url = args[:announcement_url] || "http://localhost" # http announcements
123 | http_url = "#{http_url}:4442" unless http_url.match ":[0-9]+$"
124 |
125 | console = Console.new drb_url, http_url,
126 | args[:log] || "STDOUT",
127 | args[:log_level] || Logger::INFO,
128 | args[:ping_interval] || 5,
129 | host, args[:environment], args[:event_listener]
130 |
131 | # DRb transport (galaxy command line client)
132 | Galaxy::Transport.publish drb_url, console, console.logger
133 |
134 | # HTTP API (announcements, status, ...)
135 | Galaxy::Transport.publish http_url, console, console.logger
136 |
137 | console
138 | end
139 |
140 | def shutdown
141 | Galaxy::Transport.unpublish @http_url
142 | end
143 |
144 | def join
145 | Galaxy::Transport.join @drb_url
146 | end
147 |
148 | private
149 |
150 | # Update the agents database
151 | def announce announcement
152 | begin
153 | host = announcement.host
154 | @logger.debug "Received announcement from #{host}"
155 | @mutex.synchronize do
156 | if @db.has_key?(host)
157 | unless @db[host].agent_status != "offline"
158 | announce_message = "#{host} is now online again"
159 | @logger.info announce_message
160 | @event_dispatcher.dispatch_announce_success_event announce_message
161 | end
162 | if @db[host].status != announcement.status
163 | announce_message = "#{host} core state changed: #{@db[host].status} --> #{announcement.status}"
164 | @logger.info announce_message
165 | @event_dispatcher.dispatch_announce_success_event announce_message
166 | end
167 | else
168 | announce_message = "Discovered new agent: #{host} [#{announcement.inspect}]"
169 | @logger.info "Discovered new agent: #{host} [#{announcement.inspect}]"
170 | @event_dispatcher.dispatch_announce_success_event announce_message
171 | end
172 |
173 | @db[host] = announcement
174 | @db[host].timestamp = Time.now
175 | @db[host].agent_status = 'online'
176 | end
177 | rescue RuntimeError => e
178 | error_message = "Error receiving announcement: #{e}"
179 | @logger.warn error_message
180 | @event_dispatcher.dispatch_announce_error_event error_message
181 | end
182 | end
183 |
184 | # Iterate through the database to find agents that haven't pinged home
185 | def ping cutoff
186 | @mutex.synchronize do
187 | @db.each_pair do |host, entry|
188 | if entry.agent_status != "offline" and entry.timestamp < cutoff
189 | error_message = "#{host} failed to announce; marking as offline"
190 | @logger.warn error_message
191 | @event_dispatcher.dispatch_announce_error_event error_message
192 |
193 | entry.agent_status = "offline"
194 | entry.status = "unknown"
195 | end
196 | end
197 | end
198 | end
199 | end
200 | end
201 |
--------------------------------------------------------------------------------
/lib/galaxy/controller.rb:
--------------------------------------------------------------------------------
1 | require 'logger'
2 |
3 | module Galaxy
4 | class Controller
5 | def initialize core_base, config_path, repository_base, binaries_base, log
6 | @core_base = core_base
7 | @config_path = config_path
8 | @repository_base = repository_base
9 | @binaries_base = binaries_base
10 | script = File.join(@core_base, "bin", "control")
11 | if File.exists? script
12 | @script = File.executable?(script) ? script : "/bin/sh #{script}"
13 | else
14 | raise ControllerNotFoundException.new
15 | end
16 | @log = log
17 | end
18 |
19 | def perform! command, args = ''
20 | @log.info "Invoking control script: #{@script} #{command} #{args}"
21 |
22 | begin
23 | output = `#{@script} --base #{@core_base} --binaries #{@binaries_base} --config-path #{@config_path} --repository #{@repository_base} #{command} #{args} 2>&1`
24 | rescue Exception => e
25 | raise ControllerFailureException.new(command, e)
26 | end
27 |
28 | rv = $?.exitstatus
29 |
30 | case rv
31 | when 0
32 | output
33 | when 1
34 | raise CommandFailedException.new(command, output)
35 | when 2
36 | raise UnrecognizedCommandException.new(command, output)
37 | else
38 | raise UnrecognizedResponseCodeException.new(rv, command, output)
39 | end
40 | end
41 |
42 | class ControllerException < RuntimeError;
43 | end
44 |
45 | class ControllerNotFoundException < ControllerException
46 | def initialize
47 | super "No control script available"
48 | end
49 | end
50 |
51 | class ControllerFailureException < ControllerException
52 | def initialize command, exception
53 | super "Unexpected exception executing command '#{command}': #{exception}"
54 | end
55 | end
56 |
57 | class CommandFailedException < ControllerException
58 | def initialize command, output
59 | message = "Command failed: #{command}"
60 | message += ": #{output}" unless output.empty?
61 | super message
62 | end
63 | end
64 |
65 | class UnrecognizedCommandException < ControllerException
66 | def initialize command, output
67 | message = "Unrecognized command: #{command}"
68 | message += ": #{output}" unless output.empty?
69 | super message
70 | end
71 | end
72 |
73 | class UnrecognizedResponseCodeException < ControllerException
74 | def initialize code, command, output
75 | message = "Unrecognized response code #{code} for command #{command}"
76 | message += ": #{output}" unless output.empty?
77 | super message
78 | end
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/galaxy/daemon.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # daemonize.rb is a slightly modified version of daemonize.rb was #
3 | # from the Daemonize Library written by Travis Whitton #
4 | # for details see http://grub.ath.cx/daemonize/ #
5 | ###############################################################################
6 |
7 | require 'galaxy/host'
8 | require 'fileutils'
9 |
10 | module Galaxy
11 | module Daemonize
12 | VERSION = "0.1.2"
13 |
14 | # Try to fork if at all possible retrying every 5 sec if the
15 | # maximum process limit for the system has been reached
16 | def safefork
17 | tryagain = true
18 |
19 | while tryagain
20 | tryagain = false
21 | begin
22 | if pid = fork
23 | return pid
24 | end
25 | rescue Errno::EWOULDBLOCK
26 | sleep 5
27 | tryagain = true
28 | end
29 | end
30 | end
31 |
32 | # This method causes the current running process to become a daemon
33 | # If closefd is true, all existing file descriptors are closed
34 | def daemonize(log = nil, oldmode=0, closefd=false)
35 | srand # Split rand streams between spawning and daemonized process
36 | safefork and exit # Fork and exit from the parent
37 |
38 | # Detach from the controlling terminal
39 | unless sess_id = Process.setsid
40 | raise 'Cannot detach from controlled terminal'
41 | end
42 |
43 | # Prevent the possibility of acquiring a controlling terminal
44 | if oldmode.zero?
45 | trap 'SIGHUP', 'IGNORE'
46 | exit if pid = safefork
47 | end
48 |
49 | Dir.chdir "/" # Release old working directory
50 | File.umask 0000 # Insure sensible umask
51 |
52 | if closefd
53 | # Make sure all file descriptors are closed
54 | ObjectSpace.each_object(IO) do |io|
55 | unless [STDIN, STDOUT, STDERR].include?(io)
56 | io.close rescue nil
57 | end
58 | end
59 | end
60 |
61 | log ||= "/dev/null"
62 |
63 | STDIN.reopen "/dev/null" # Free file descriptors and
64 | STDOUT.reopen log, "a" # point them somewhere sensible
65 | STDERR.reopen STDOUT # STDOUT/STDERR should go to a logfile
66 | return oldmode ? sess_id : 0 # Return value is mostly irrelevant
67 | end
68 | end
69 |
70 | class Daemon
71 | include Galaxy::Daemonize
72 |
73 | def self.pid_for pid_file
74 | begin
75 | File.open(pid_file) do |f|
76 | f.gets
77 | end.to_i
78 | rescue Errno::ENOENT
79 | return nil
80 | end
81 | end
82 |
83 | def self.kill_daemon pid_file
84 | pid = pid_for(pid_file)
85 | if pid.nil?
86 | raise "Cannot determine process id: Pid file #{pid_file} not found"
87 | end
88 | begin
89 | Process.kill("TERM", pid)
90 | rescue Errno::ESRCH
91 | raise "Cannot kill process id #{pid}: Not running"
92 | rescue Errno::EPERM
93 | raise "Cannot kill process id #{pid}: Permission denied"
94 | end
95 | end
96 |
97 | def self.daemon_running? pid_file
98 | pid = pid_for(pid_file)
99 | if pid.nil?
100 | return false
101 | end
102 | begin
103 | Process.kill(0, pid)
104 | rescue Errno::ESRCH
105 | return false
106 | rescue Errno::EPERM
107 | return true
108 | end
109 | return true
110 | end
111 |
112 | def initialize & block
113 | @block = block
114 | end
115 |
116 | def go pid_file, log
117 | daemonize(log)
118 |
119 | File.open(pid_file, "w", 0644) do |f|
120 | f.write Process.pid
121 | end
122 | trap "TERM" do
123 | FileUtils.rm_f pid_file
124 | exit 0
125 | end
126 | trap "KILL" do
127 | FileUtils.rm_f pid_file
128 | exit 0
129 | end
130 | @block.call
131 | end
132 |
133 | def self.start name, pid_file, user = nil, log = nil, & block
134 | Galaxy::HostUtils::switch_user(user) unless user.nil?
135 | if daemon_running?(pid_file)
136 | pid = pid_for(pid_file)
137 | abort("Error: #{name} is already running as pid #{pid}")
138 | end
139 | Daemon.new(& block).go(pid_file, log)
140 | end
141 | end
142 | end
143 |
--------------------------------------------------------------------------------
/lib/galaxy/db.rb:
--------------------------------------------------------------------------------
1 | require 'thread'
2 |
3 | module Galaxy
4 | class DB
5 | def initialize path
6 | @lock = Mutex.new
7 | @path = path
8 | Dir.mkdir @path rescue nil
9 | end
10 |
11 | def delete_at key
12 | @lock.synchronize do
13 | FileUtils.rm_f file_for(key)
14 | end
15 | end
16 |
17 | def []= key, value
18 | @lock.synchronize do
19 | File.open file_for(key), "w" do |f|
20 | f.write(value)
21 | end
22 | end
23 | end
24 |
25 | def [] key
26 | @lock.synchronize do
27 | result = nil
28 | begin
29 | File.open file_for(key), "r" do |f|
30 | result = f.read
31 | end
32 | rescue Errno::ENOENT
33 | end
34 |
35 | return result
36 | end
37 | end
38 |
39 | def file_for key
40 | File.join(@path, key)
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/galaxy/deployer.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 | require 'tempfile'
3 | require 'logger'
4 | require 'galaxy/host'
5 |
6 | module Galaxy
7 | class Deployer
8 | attr_reader :log
9 |
10 | def initialize deploy_dir, log
11 | @base, @log = deploy_dir, log
12 | end
13 |
14 | # number is the deployment number for this agent
15 | # archive is the path to the binary archive to deploy
16 | # props are the properties (configuration) for the core
17 | def deploy number, archive, config_path, repository_base, binaries_base
18 | core_base = File.join(@base, number.to_s);
19 | FileUtils.mkdir_p core_base
20 |
21 | log.info "deploying #{archive} to #{core_base} with config path #{config_path}"
22 |
23 | command = "#{Galaxy::HostUtils.tar} -C #{core_base} -zxf #{archive}"
24 | begin
25 | Galaxy::HostUtils.system command
26 | rescue Galaxy::HostUtils::CommandFailedError => e
27 | raise "Unable to extract archive: #{e.message}"
28 | end
29 |
30 | xndeploy = "#{core_base}/bin/xndeploy"
31 | unless FileTest.executable? xndeploy
32 | xndeploy = "/bin/sh #{xndeploy}"
33 | end
34 |
35 | command = "#{xndeploy} --base #{core_base} --binaries #{binaries_base} --config-path #{config_path} --repository #{repository_base}"
36 | begin
37 | Galaxy::HostUtils.system command
38 | rescue Galaxy::HostUtils::CommandFailedError => e
39 | raise "Deploy script failed: #{e.message}"
40 | end
41 | return core_base
42 | end
43 |
44 | def activate number
45 | core_base = File.join(@base, number.to_s);
46 | current = File.join(@base, "current")
47 | if File.exists? current
48 | File.unlink(current)
49 | end
50 | FileUtils.ln_sf core_base, current
51 | return core_base
52 | end
53 |
54 | def deactivate number
55 | current = File.join(@base, "current")
56 | if File.exists? current
57 | File.unlink(current)
58 | end
59 | end
60 |
61 | def rollback number
62 | current = File.join(@base, "current")
63 |
64 | if File.exists? current
65 | File.unlink(current)
66 | end
67 |
68 | FileUtils.rm_rf File.join(@base, number.to_s)
69 |
70 | core_base = File.join(@base, (number - 1).to_s)
71 | FileUtils.ln_sf core_base, current
72 |
73 | return core_base
74 | end
75 |
76 | def cleanup_up_to_previous current, db
77 | # Keep the current and last one (for rollback)
78 | (1..(current - 2)).each do |number|
79 | key = number.to_s
80 |
81 | # Remove deployed bits
82 | FileUtils.rm_rf File.join(@base, key)
83 |
84 | # Cleanup the database
85 | db.delete_at key
86 | end
87 | end
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/lib/galaxy/events.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'galaxy/announcements'
3 |
4 | module Galaxy
5 | # Generic Event sender class
6 | class EventSender
7 | COLLECTOR_API_VERSION = 1
8 |
9 | GALAXY_SCHEMA = 'Galaxy'
10 | GALAXY_LOG_SCHEMA = 'GalaxyLog'
11 | DUMMY_TYPE = 'dummy'
12 |
13 | attr_reader :type, :log
14 |
15 | def initialize(listener_url, gonsole_url = nil, ip_addr=nil, log=Logger.new(STDOUT))
16 | @log = log
17 | @gonsole_url = gonsole_url
18 | @ip_addr = ip_addr
19 | @log.debug "Registered Event listener type #{self.class} at #{listener_url}, sender url #{ip_addr}"
20 | listener_url.nil? ? @uri = nil : @uri = URI.parse(listener_url)
21 | end
22 |
23 | # To override in the child class. event is an OpenStruct
24 | def send_event(event)
25 | # no-op
26 | end
27 |
28 | private
29 |
30 | def escape str
31 | # default is URI::REGEXP::UNSAFE is /[^-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]/n
32 | # and is not enough! (& and , not escaped)
33 | return URI.escape(str, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
34 | end
35 |
36 | # Sanitize strings
37 | def add_field(type, value)
38 | return "#{type}#{escape(value.to_s)},"
39 | end
40 |
41 | def do_send_event(formatted_query)
42 | return if @uri.nil?
43 | http_query = @uri.merge(formatted_query).to_s
44 | @log.debug http_query
45 | begin
46 | # GET
47 | res = Net::HTTP.start(@uri.host, @uri.port) do |http|
48 | headers = {'Content-Type' => 'text/plain; charset=utf-8'}
49 | response = http.send_request('GET', http_query, nil, headers)
50 | @log.debug "Event sent to Collector #{@uri.host} = #{http_query}" if @log
51 | response # Return the response form the block
52 | end
53 | case res
54 | when Net::HTTPAccepted
55 | return true
56 | else
57 | res.error!
58 | end
59 | rescue Exception => e
60 | if @log
61 | @log.warn "Unable to contact EventListener on #{@uri}"
62 | @log.warn "Request: #{http_query}"
63 | @log.warn "Client side error: #{e}"
64 | @log.warn "Body reponse: #{res.body}" if res
65 | end
66 | return false
67 | end
68 | end
69 | end
70 |
71 | # Send logs to HDFS
72 | class GalaxyLogEventSender < EventSender
73 | EVENTS_SUPPORTED = [:debug, :error, :fatal, :info, :warn]
74 |
75 | EVENTS_SUPPORTED.each do |loglevel|
76 | define_method "dispatch_#{loglevel.to_s}_log" do |* args|
77 | message, progname, * ignored = args
78 | send_event(generate_event(loglevel.to_s, message, progname))
79 | end
80 | end
81 |
82 | private
83 |
84 | # Pre-process logs and generate OpenStruct mapping the GalaxyLog Thrift Schema
85 | #
86 | # struct GalaxyLog {
87 | # 1:i64 date,
88 | # 2:i32 ip_addr,
89 | # 3:i16 pid,
90 | # 4:string severity,
91 | # 5:string progname,
92 | # 6:string message
93 | #}
94 | def generate_event(severity, message, progname)
95 | event = OpenStruct.new
96 |
97 | event.date = Time.now.to_i * 1000
98 | event.gonsole_url = @gonsole_url
99 | event.ip_addr = @ip_addr
100 | event.pid = $$
101 | event.severity = severity.downcase
102 | event.progname = progname
103 | event.message = message
104 |
105 | return event
106 | end
107 |
108 | def send_event(event)
109 | do_send_event format_url(event)
110 | end
111 |
112 | private
113 |
114 | def format_url(event)
115 | url = "/#{COLLECTOR_API_VERSION}?"
116 | url += "v=#{EventSender::GALAXY_LOG_SCHEMA},"
117 | url += escape(add_field("8", event.date))
118 | # Make sure to add a valid number (int) or the collector will choke on it (400 bad request)
119 | url += escape(add_field("4", (format_ip(event.ip_addr))))
120 | url += escape(add_field("2", (event.pid || 0)))
121 | url += escape(add_field("s", event.severity))
122 | url += escape(add_field("s", event.progname))
123 | url += escape(add_field("s", event.message))
124 | url += escape(add_field("s", event.gonsole_url))
125 | url += "&rt=b"
126 | return url
127 | end
128 |
129 | def format_ip(ip_addr)
130 | if ip_addr.nil? or ip_addr.empty?
131 | return 0
132 | end
133 | addr = 0
134 | ip_addr.split(".").each do |x|
135 | addr = addr * 256 + x.to_i
136 | end
137 | return addr
138 | end
139 | end
140 |
141 | # Send actions related events to HDFS
142 | class GalaxyEventSender < EventSender
143 | EVENTS_SUPPORTED = [:announce, :cleanup, :command, :update_config, :become, :rollback, :start, :stop, :clear, :perform, :restart]
144 | EVENTS_RESULT_SUPPORTED = [:success, :error]
145 |
146 | EVENTS_SUPPORTED.each do |event|
147 | EVENTS_RESULT_SUPPORTED.each do |result|
148 | define_method "dispatch_#{event}_#{result}_event" do |status|
149 | if status.is_a? String
150 | status = OpenStruct.new(:message => status)
151 | elsif status.is_a? Hash
152 | status = OpenStruct.new(status)
153 | end
154 | # e.g. perform, announce
155 | status.event_type = event
156 | # e.g. error, success
157 | status.galaxy_event_type = result
158 | status.gonsole_url = @gonsole_url
159 | send_event(status)
160 | end
161 | end
162 | end
163 |
164 | def send_event(event)
165 | do_send_event format_url(event)
166 | end
167 |
168 | private
169 |
170 | def format_url(event)
171 | url = "/#{COLLECTOR_API_VERSION}?"
172 | url += "v=#{EventSender::GALAXY_SCHEMA},"
173 | #url += add_field "8", event.timestamp
174 | url += escape(add_field("x", "date"))
175 | url += escape(add_field("s", event.event_type))
176 | url += escape(add_field("s", event.message))
177 | url += escape(add_field("s", event.agent_status))
178 | url += escape(add_field("s", event.os))
179 | url += escape(add_field("s", event.host))
180 | url += escape(add_field("s", event.galaxy_version))
181 | url += escape(add_field("s", event.core_type))
182 | url += escape(add_field("s", event.machine))
183 | url += escape(add_field("s", event.status))
184 | url += escape(add_field("s", event.galaxy_event_type))
185 | url += escape(add_field("s", event.url))
186 | url += escape(add_field("s", event.config_path))
187 | url += escape(add_field("s", event.build))
188 | url += escape(add_field("s", event.ip))
189 | url += escape(add_field("s", event.user))
190 | url += escape(add_field("s", event.gonsole_url))
191 | url += "&rt=b"
192 | return url
193 | end
194 | end
195 |
196 | # When the user doesn't specify a collector URL
197 | class DummyEventSender < EventSender
198 | def initialize()
199 | end
200 |
201 | def method_missing(m, * args, & block)
202 | end
203 | end
204 | end
205 |
--------------------------------------------------------------------------------
/lib/galaxy/fetcher.rb:
--------------------------------------------------------------------------------
1 | require 'galaxy/temp'
2 | require 'galaxy/host'
3 |
4 | module Galaxy
5 | class Fetcher
6 | def initialize base_url, http_user, http_password, log
7 | @base, @http_user, @http_password, @log = base_url, http_user, http_password, log
8 | end
9 |
10 | # return path on filesystem to the binary
11 | def fetch type, version, extension="tar.gz"
12 | core_url = "#{@base}/#{type}-#{version}.#{extension}"
13 | tmp = Galaxy::Temp.mk_auto_file "galaxy-download"
14 | @log.info("Fetching #{core_url} into #{tmp}")
15 | if @base =~ /^https?:/
16 | begin
17 | curl_command = "curl -D - #{core_url} -o #{tmp} -s"
18 | if !@http_user.nil? && !@http_password.nil?
19 | curl_command << " -u #{@http_user}:#{@http_password}"
20 | end
21 |
22 | @log.debug("Running CURL command: #{curl_command}")
23 | output = Galaxy::HostUtils.system(curl_command)
24 | rescue Galaxy::HostUtils::CommandFailedError => e
25 | raise "Failed to download archive #{core_url}: #{e.message}"
26 | end
27 | status = output.first
28 | (protocol, response_code, response_message) = status.split
29 | unless response_code == '200'
30 | raise "Failed to download archive #{core_url}: #{status}"
31 | end
32 | else
33 | open(core_url) do |io|
34 | File.open(tmp, "w") { |f| f.write(io.read) }
35 | end
36 | end
37 | return tmp
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/galaxy/filter.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Filter
3 | def self.new args
4 | filters = []
5 |
6 | case args[:set]
7 | when :all, "all"
8 | filters << lambda { true }
9 | when :empty, "empty"
10 | filters << lambda { |a| a.config_path.nil? }
11 | when :taken, "taken"
12 | filters << lambda { |a| a.config_path }
13 | end
14 |
15 | if args[:env] || args[:version] || args[:type]
16 | env = args[:env] || "[^/]+"
17 | version = args[:version] || "[^/]+"
18 | type = args[:type] || ".+"
19 |
20 | filters << lambda { |a| a.config_path =~ %r!^/#{env}/#{version}/#{type}$! }
21 | end
22 |
23 | if args[:host]
24 | filters << lambda { |a| a.host == args[:host] }
25 | end
26 |
27 | if args[:ip]
28 | filters << lambda { |a| a.ip == args[:ip] }
29 | end
30 |
31 | if args[:machine]
32 | filters << lambda { |a| a.machine == args[:machine] }
33 | end
34 |
35 | if args[:state]
36 | filters << lambda { |a| a.status == args[:state] }
37 | end
38 |
39 | if args[:agent_state]
40 | p args[:agent_state]
41 | filters << lambda { |a| p a.agent_status; a.agent_status == args[:agent_state] }
42 | end
43 |
44 | lambda do |a|
45 | filters.inject(false) { |result, filter| result || filter.call(a) }
46 | end
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/lib/galaxy/host.rb:
--------------------------------------------------------------------------------
1 | require 'tempfile'
2 | require 'syslog'
3 | require 'logger'
4 |
5 | module Galaxy
6 | module HostUtils
7 | def HostUtils.logger ident="galaxy"
8 | @logger ||= begin
9 | log = Syslog.open ident, Syslog::LOG_PID | Syslog::LOG_CONS, Syslog::LOG_LOCAL7
10 | class << log
11 | attr_reader :level
12 | # The interface is busted between Logger and Syslog. The later expects a format string. The former a string.
13 | # This was breaking logging in the event code in production (we log the url, which contains escaped characters).
14 | # Poor man's solution: assume the message is not a format string if we pass only one argument.
15 | #
16 | alias_method :unsafe_debug, :debug
17 |
18 | def debug * args
19 | args.length == 1 ? unsafe_debug(safe_format(args[0])) : unsafe_debug(* args)
20 | end
21 |
22 | alias_method :unsafe_info, :info
23 |
24 | def info * args
25 | args.length == 1 ? unsafe_info(safe_format(args[0])) : unsafe_info(* args)
26 | end
27 |
28 | def warn * args
29 | args.length == 1 ? warning(safe_format(args[0])) : warning(* args)
30 | end
31 |
32 | def error * args
33 | args.length == 1 ? err(safe_format(args[0])) : err(* args)
34 | end
35 |
36 | # set log levels from standard Logger levels
37 | def level=(val)
38 | @level = val
39 | case val # Note that there are other log levels: LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_NOTICE
40 | when Logger::ERROR
41 | Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_ERR)
42 | when Logger::WARN
43 | Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_WARNING)
44 | when Logger::DEBUG
45 | Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_DEBUG)
46 | when Logger::INFO
47 | Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_INFO)
48 | end
49 | end
50 |
51 | def safe_format(arg)
52 | return arg.gsub("%", "%%")
53 | end
54 |
55 | # The logger implementation dump msg directly, without appending any loglevel. We need one though for Syslog.
56 | # By default, logger(1) uses ``user.notice''. Do the same here.
57 | def <<(msg)
58 | notice(msg)
59 | end
60 | end
61 | log.level = Logger::INFO
62 | log
63 | end
64 | end
65 |
66 | # Returns the name of the user that invoked the command
67 | #
68 | # This implementation tries +who am i+, available on some unix platforms, to check the owner of the controlling terminal,
69 | # which preserves ownership across +su+ and +sudo+. Failing that, the environment is checked for a +USERNAME+ or +USER+ variable.
70 | # Finally, the system password database is consulted.
71 | def HostUtils.shell_user
72 | guesses = []
73 | guesses << `who am i 2> /dev/null`.split[0]
74 | guesses << ENV['USERNAME']
75 | guesses << ENV['USER']
76 | guesses << Etc.getpwuid(Process.uid).name
77 | guesses.first { |guess| notguess.nil? and notguess.empty? }
78 | end
79 |
80 | def HostUtils.avail_path
81 | @avail_path ||= begin
82 | directories = %w{ /usr/local/var/galaxy /var/galaxy /var/tmp /tmp }
83 | directories.find { |dir| FileTest.writable? dir }
84 | end
85 | end
86 |
87 | def HostUtils.tar
88 | @tar ||= begin
89 | unless `which gtar` =~ /^no gtar/ || `which gtar`.length == 0
90 | "gtar"
91 | else
92 | "tar"
93 | end
94 | end
95 | end
96 |
97 | def HostUtils.switch_user user
98 | pwent = Etc::getpwnam(user)
99 | uid, gid = pwent.uid, pwent.gid
100 | if Process.gid != gid or Process.uid != uid
101 | Process::GID::change_privilege(gid)
102 | Process::initgroups(user, gid)
103 | Process::UID::change_privilege(uid)
104 | end
105 | if Process.gid != gid or Process.uid != uid
106 | abort("Error: unable to switch user to #{user}")
107 | end
108 | end
109 |
110 | class CommandFailedError < Exception
111 | attr_reader :command, :exitstatus, :output
112 |
113 | def initialize command, exitstatus, output
114 | @command = command
115 | @exitstatus = exitstatus
116 | @output = output
117 | end
118 |
119 | def message
120 | "Command '#{@command}' exited with status code #{@exitstatus} and output: #{@output}".chomp()
121 | end
122 | end
123 |
124 | # An alternative to Kernel.system that invokes a command, raising an exception containing
125 | # the command's stdout and stderr if the command returns a status code other than 0
126 | def HostUtils.system command
127 | output = IO.popen("#{command} 2>&1") { |io| io.readlines }
128 | unless $?.success?
129 | raise CommandFailedError.new(command, $?.exitstatus, output)
130 | end
131 | output
132 | end
133 | end
134 | end
135 |
--------------------------------------------------------------------------------
/lib/galaxy/log.rb:
--------------------------------------------------------------------------------
1 | require 'galaxy/events'
2 | require 'galaxy/host'
3 |
4 | module Galaxy
5 | module Log
6 | class Glogger
7 | attr_reader :log, :event_dispatcher
8 |
9 | def initialize(logdev, event_listener = nil, gonsole_url = nil, ip_addr = nil, shift_age = 0, shift_size = 1048576)
10 | @gonsole_url = gonsole_url
11 |
12 | case logdev
13 | when "SYSLOG"
14 | @log = Galaxy::HostUtils.logger "galaxy"
15 | when "STDOUT"
16 | @log = Logger.new(STDOUT, shift_age, shift_size)
17 | when "STDERR"
18 | @log = Logger.new(STDERR, shift_age, shift_size)
19 | else
20 | @log = Logger.new(logdev, shift_age, shift_size)
21 | end
22 |
23 | if event_listener.nil?
24 | @event_dispatcher = Galaxy::DummyEventSender.new()
25 | else
26 | @event_dispatcher = Galaxy::GalaxyLogEventSender.new(event_listener, @gonsole_url, ip_addr, @log)
27 | end
28 | end
29 |
30 | def debug(progname = nil, & block)
31 | @event_dispatcher.dispatch_debug_log(format_message(progname, & block)) if @log.level <= Logger::DEBUG
32 | @log.debug progname, & block
33 | end
34 |
35 | def error(progname = nil, & block)
36 | @event_dispatcher.dispatch_error_log(format_message(progname, & block)) if @log.level <= Logger::ERROR
37 | @log.error progname, & block
38 | end
39 |
40 | def fatal(progname = nil, & block)
41 | @event_dispatcher.dispatch_fatal_log(format_message(progname, & block)) if @log.level <= Logger::FATAL
42 | @log.fatal progname, & block
43 | end
44 |
45 | def info(progname = nil, & block)
46 | @event_dispatcher.dispatch_info_log(format_message(progname, & block)) if @log.level <= Logger::INFO
47 | @log.info progname, & block
48 | end
49 |
50 | def warn(progname = nil, & block)
51 | @event_dispatcher.dispatch_warn_log(format_message(progname, & block)) if @log.level <= Logger::WARN
52 | @log.warn progname, & block
53 | end
54 |
55 | # Pipeline other methods to Logger
56 | # We don't want to define << for instance: when we'll end up having a dedicated Thrift
57 | # schema for logs, we do want to know beforehand the expected formatting.
58 | def method_missing(m, * args, & block)
59 | @log.send m, * args, & block
60 | end
61 |
62 | private
63 |
64 | def format_message(progname, & block)
65 | if block_given?
66 | message = yield
67 | else
68 | message = progname
69 | end
70 | message
71 | end
72 | end
73 |
74 | class LoggerIO < IO
75 | require 'strscan'
76 |
77 | def initialize log, level = :info
78 | @log = log
79 | @level = level
80 | @buffer = ""
81 | end
82 |
83 | def write str
84 | @buffer << str
85 |
86 | scanner = StringScanner.new(@buffer)
87 |
88 | while scanner.scan(/([^\n]*)\n/)
89 | line = scanner[1]
90 | case @level
91 | when :warn
92 | @log.warn line
93 | when :info
94 | @log.info line
95 | when :error
96 | @log.error line
97 | end
98 | end
99 |
100 | @buffer = scanner.rest
101 | end
102 | end
103 | end
104 | end
105 |
106 | if __FILE__ == $0
107 | def a
108 | b
109 | end
110 |
111 | def b
112 | raise "error"
113 | end
114 |
115 | require 'logger'
116 |
117 | log = Logger.new(STDERR)
118 | info = Galaxy::Log::LoggerIO.new log, :info
119 | warn = Galaxy::Log::LoggerIO.new log, :error
120 | $stdout = info
121 | $stderr = warn
122 |
123 | puts "hello world\nbye bye"
124 |
125 | a
126 | end
127 |
--------------------------------------------------------------------------------
/lib/galaxy/parallelize.rb:
--------------------------------------------------------------------------------
1 | require 'thread'
2 |
3 | class CountingSemaphore
4 |
5 | def initialize(initvalue = 0)
6 | @counter = initvalue
7 | @waiting_list = []
8 | end
9 |
10 | def wait
11 | Thread.exclusive {
12 | @counter -= 1
13 | if @counter < 0
14 | @waiting_list.push(Thread.current)
15 | Thread.stop
16 | end
17 | }
18 | self
19 | end
20 |
21 | def signal
22 | Thread.exclusive {
23 | begin
24 | @counter += 1
25 | if @counter <= 0
26 | t = @waiting_list.shift
27 | t.wakeup if t
28 | end
29 | rescue ThreadError
30 | retry
31 | end
32 | }
33 | self
34 | ensure
35 | Thread.critical = false
36 | end
37 |
38 | def exclusive
39 | wait
40 | yield
41 | ensure
42 | signal
43 | end
44 |
45 | end
46 |
47 | class ThreadGroup
48 |
49 | def join
50 | list.each { |t| t.join }
51 | end
52 |
53 | def << thread
54 | add thread
55 | end
56 |
57 | def kill
58 | list.each { |t| t.kill }
59 | end
60 |
61 | end
62 |
63 | # execute in parallel with up to thread_count threads at once
64 | class Array
65 | def parallelize(thread_count = 100)
66 | sem = CountingSemaphore.new(thread_count ? thread_count : 100)
67 | results = []
68 | threads = ThreadGroup.new
69 | lock = Mutex.new
70 | each_with_index do |item, i|
71 | sem.wait
72 | threads << Thread.new do
73 | begin
74 | yield item
75 | ensure
76 | sem.signal
77 | end
78 | end
79 | end
80 |
81 | threads.join
82 |
83 | results
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/lib/galaxy/properties.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'open-uri'
3 | require 'logger'
4 |
5 | module Galaxy
6 | module Properties
7 |
8 | def Properties.parse_props io, props={}
9 | io.each_line do |line|
10 | if line =~ /^(\s)*#/
11 | # comment, ignore
12 | elsif line =~ /^([^=]+)\s*=(.*)$/
13 | props[$1.strip] = $2.strip
14 | end
15 |
16 | end
17 | props
18 | end
19 |
20 | class Builder
21 | def initialize base, http_user, http_password, log=Logger.new(STDOUT)
22 | @base = base
23 | @log = log
24 | if !http_user.nil? && !http_password.nil?
25 | @http_auth = {:http_basic_authentication =>[http_user, http_password]}
26 | end
27 | end
28 |
29 | def build hierarchy, file_name
30 | props = {}
31 |
32 | hierarchy.split(/\//).inject([]) do |history, part|
33 | history << part
34 | begin
35 |
36 | url = "#{@base}#{history.join("/")}/#{file_name}"
37 | @log.debug "Fetching #{url}"
38 |
39 | auth = {}
40 | begin
41 | fetch_done = true
42 | open(url, auth) do |io|
43 | Properties.parse_props io, props
44 | end
45 | rescue OpenURI::HTTPError => http_err
46 | if http_err.io.status[0] == "401" && auth.empty? && !@http_auth.nil?
47 | # retry with the auth information
48 | auth = @http_auth
49 | fetch_done = false
50 | else
51 | raise http_err
52 | end
53 | end while !fetch_done
54 | rescue => e
55 | @log.debug e.message
56 | end
57 | history
58 | end
59 | @log.debug props.inspect
60 | props
61 | end
62 |
63 | def replace_tokens properties, tokens
64 | # replace special tokens
65 | # syntax is #{TOKEN}
66 | # (old syntax of $TOKEN is deprecated)
67 | properties.inject({}) do |hash, pair|
68 | key, value = pair
69 | hash[key] = value
70 | tokens.each { |find, replace| hash[key] = hash[key].gsub("$#{find}", replace).gsub("\#{#{find}}", replace) }
71 | hash
72 | end
73 | end
74 | end
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/lib/galaxy/proxy_console.rb:
--------------------------------------------------------------------------------
1 | require 'ostruct'
2 | require 'logger'
3 | require 'galaxy/filter'
4 | require 'galaxy/transport'
5 |
6 | module Galaxy
7 | class ProxyConsole
8 | attr_reader :db
9 | @@max_conn_failures = 3
10 |
11 | def initialize drb_url, console_url, log, log_level, ping_interval
12 | @log =
13 | case log
14 | when "SYSLOG"
15 | Galaxy::HostUtils.logger "galaxy-console"
16 | when "STDOUT"
17 | Logger.new STDOUT
18 | when "STDERR"
19 | Logger.new STDERR
20 | else
21 | Logger.new log
22 | end
23 | @log.level = log_level
24 | @drb_url = drb_url
25 | @ping_interval = ping_interval
26 | @db = {}
27 | @mutex = Mutex.new
28 | @conn_failures = 0
29 |
30 | @console_proxyied_url = console_url
31 | @console_proxied = Galaxy::Transport.locate(console_url)
32 |
33 | Thread.new do
34 | loop do
35 | begin
36 | sleep @ping_interval
37 | synchronize
38 | if @conn_failures > 0
39 | @log.warn "Communication with the master gonsole re-established"
40 | end
41 | # Reset the number of connection failures
42 | @conn_failures = 0
43 | rescue DRb::DRbConnError => e
44 | @conn_failures += 1
45 | @log.warn "Unable to communicate with the master gonsole (#{@conn_failures})"
46 | if @conn_failures >= @@max_conn_failures
47 | @log.error "Number of connection failures reached"
48 | shutdown
49 | exit("Connection Error")
50 | end
51 | retry
52 | rescue Exception => e
53 | @log.warn "Uncaught exception in agent ping thread: #{e}"
54 | @log.warn e.backtrace
55 | abort("Unkown Error")
56 | end
57 | end
58 | end
59 | end
60 |
61 | def synchronize
62 | @mutex.synchronize do
63 | @db = @console_proxied.db
64 | @log.info "Synchronized with master gonsole at #{@console_proxyied_url}"
65 | @log.debug "Got new db: #{@db}"
66 | end
67 | end
68 |
69 | # Remote API
70 | def agents filters = {:set => :all}
71 | filter = Galaxy::Filter.new filters
72 | @mutex.synchronize do
73 | @db.values.select(& filter)
74 | end
75 | end
76 |
77 | # Remote API
78 | def log msg
79 | @log.info msg
80 | end
81 |
82 | def ProxyConsole.start args
83 | host = args[:host] || "localhost"
84 | drb_url = args[:url] || "druby://" + host # DRB transport
85 | drb_url += ":4440" unless drb_url.match ":[0-9]+$"
86 |
87 | console_proxyied_url = args[:console_proxyied_url] || "druby://localhost"
88 | console_proxyied_url += ":4440" unless console_proxyied_url.match ":[0-9]+$"
89 |
90 | console = ProxyConsole.new drb_url, console_proxyied_url,
91 | args[:log] || "STDOUT",
92 | args[:log_level] || Logger::INFO,
93 | args[:ping_interval] || 5
94 |
95 | Galaxy::Transport.publish drb_url, console # DRB transport
96 | console
97 | end
98 |
99 | def shutdown
100 | Galaxy::Transport.unpublish @drb_url
101 | end
102 |
103 | def join
104 | Galaxy::Transport.join @drb_url
105 | end
106 | end
107 | end
108 |
--------------------------------------------------------------------------------
/lib/galaxy/report.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Client
3 | class Report
4 | def initialize
5 | @buffer = ""
6 | end
7 |
8 | def start
9 | end
10 |
11 | def record_result result
12 | @buffer += sprintf(format_string, * format_result(result))
13 | end
14 |
15 | def finish
16 | @buffer.length > 0 ? @buffer : nil
17 | end
18 |
19 | private
20 |
21 | def format_string
22 | "%s\n"
23 | end
24 |
25 | def format_result result
26 | [result]
27 | end
28 | end
29 |
30 | class ConsoleStatusReport < Report
31 | private
32 |
33 | def format_string
34 | "%s\t%s\t%s\t%s\t%s\n"
35 | end
36 |
37 | def format_field field
38 | field ? field : '-'
39 | end
40 |
41 | def format_result result
42 | [
43 | format_field(result.drb_url),
44 | format_field(result.http_url),
45 | format_field(result.host),
46 | format_field(result.env),
47 | format_field(result.ping_interval),
48 | ]
49 | end
50 | end
51 |
52 | class AgentStatusReport < Report
53 | private
54 |
55 | def format_string
56 | STDOUT.tty? ? "%-20s %-8s %-10s\n" : "%s\t%s\t%s\n"
57 | end
58 |
59 | def format_field field
60 | field ? field : '-'
61 | end
62 |
63 | def format_result result
64 | [
65 | format_field(result.host),
66 | format_field(result.agent_status),
67 | format_field(result.galaxy_version),
68 | ]
69 | end
70 | end
71 |
72 | class SoftwareDeploymentReport < Report
73 | private
74 |
75 | def format_string
76 | STDOUT.tty? ? "%-20s %-45s %-10s %-15s %-20s %-20s %-15s %-8s\n" : "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
77 | end
78 |
79 | def format_field field
80 | field ? field : '-'
81 | end
82 |
83 | def format_result result
84 | [
85 | format_field(result.host),
86 | format_field(result.config_path),
87 | format_field(result.status),
88 | format_field(result.build),
89 | format_field(result.core_type),
90 | format_field(result.machine),
91 | format_field(result.ip),
92 | format_field(result.agent_status),
93 | ]
94 | end
95 | end
96 |
97 | class CoreStatusReport < Report
98 | private
99 |
100 | def format_string
101 | STDOUT.tty? ? "%-20s %-45s %-10s %-15s %-20s %-14s\n" : "%s\t%s\t%s\t%s\t%s\t%s\n"
102 | end
103 |
104 | def format_field field
105 | field ? field : '-'
106 | end
107 |
108 | def format_result result
109 | [
110 | format_field(result.host),
111 | format_field(result.config_path),
112 | format_field(result.status),
113 | format_field(result.build),
114 | format_field(result.core_type),
115 | format_field(result.last_start_time),
116 | ]
117 | end
118 | end
119 |
120 | class LocalSoftwareDeploymentReport < Report
121 | private
122 |
123 | def format_string
124 | STDOUT.tty? ? "%-45s %-10s %-15s %-20s %s\n" : "%s\t%s\t%s\t%s\t%s\n"
125 | end
126 |
127 | def format_field field
128 | field ? field : '-'
129 | end
130 |
131 | def format_result result
132 | [
133 | format_field(result.config_path),
134 | format_field(result.status),
135 | format_field(result.build),
136 | format_field(result.core_type),
137 | "autostart=#{result.auto_start}",
138 | ]
139 | end
140 | end
141 |
142 | class CommandOutputReport < Report
143 | def initialize
144 | super
145 | @software_deployment_report = SoftwareDeploymentReport.new
146 | end
147 |
148 | def record_result result
149 | @software_deployment_report.record_result(result[0])
150 | host, output = format_result(result)
151 | output.split("\n").each { |line| @buffer += sprintf(format_string, host, line) }
152 | end
153 |
154 | private
155 |
156 | def format_string
157 | "%-20s %s\n"
158 | end
159 |
160 | def format_result result
161 | status, output = result
162 | return "#{status.host}:", output
163 | end
164 | end
165 | end
166 | end
167 |
--------------------------------------------------------------------------------
/lib/galaxy/repository.rb:
--------------------------------------------------------------------------------
1 | require 'open-uri'
2 | require 'logger'
3 |
4 | module Galaxy
5 | class Repository
6 | def initialize base, log=Logger.new(STDOUT)
7 | @base = base
8 | end
9 |
10 | def walk hierarchy, file_name
11 | result = []
12 | hierarchy.split(/\//).inject([]) do |history, part|
13 | history << part
14 | begin
15 | path = "#{history.join("/")}/#{file_name}"
16 | url = "#{@base}#{path}"
17 | open(url) do |io|
18 | data = io.read
19 | if block_given?
20 | yield path, data
21 | end
22 | result << data
23 | end
24 | rescue
25 | end
26 | history
27 | end
28 |
29 | result
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/galaxy/software.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | class SoftwareExecutable
3 | attr_accessor :type, :version
4 |
5 | def initalize type, version
6 | @type = type
7 | @version = version
8 | end
9 | end
10 |
11 | class SoftwareConfiguration
12 | attr_accessor :environment, :version, :type
13 |
14 | def initialize environment, version, type
15 | @environment = environment
16 | @version = version
17 | @type = type
18 | end
19 |
20 | def config_path
21 | "/#{environment}/#{version}/#{type}"
22 | end
23 |
24 | def self.new_from_config_path config_path
25 | # Using ! as regex delimiter since the config path contains / characters
26 | unless components = %r!^/([^/]+)/([^/]+)/(.*)$!.match(config_path)
27 | raise "Illegal config path '#{config_path}'"
28 | end
29 | environment, version, type = components[1], components[2], components[3]
30 | new environment, version, type
31 | end
32 | end
33 |
34 | class SoftwareDeployment
35 | attr_accessor :executable, :config, :running_state
36 |
37 | def initialize executable, config, running_state
38 | @executable = executable
39 | @config = config
40 | @running_state = running_state
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/galaxy/starter.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 | require 'galaxy/host'
3 | require 'logger'
4 |
5 | module Galaxy
6 | class Starter
7 | def initialize log
8 | @log = log
9 | end
10 |
11 | [:start!, :restart!, :stop!, :status].each do |action|
12 | define_method action.to_s do |path|
13 | return "unknown" if path.nil?
14 | launcher_path = xnctl_path(path)
15 |
16 | command = "#{launcher_path} #{action.to_s.chomp('!')}"
17 | @log.debug "Running #{command}"
18 | begin
19 | output = Galaxy::HostUtils.system command
20 | @log.debug "#{command} returned: #{output}"
21 | # Command returned 0, return status of the app
22 | case action
23 | when :start!
24 | when :restart!
25 | return "running"
26 | when :stop!
27 | when :status
28 | return "stopped"
29 | else
30 | return "unknown"
31 | end
32 | rescue Galaxy::HostUtils::CommandFailedError => e
33 | # status is special
34 | if action == :status
35 | if e.exitstatus == 1
36 | return "running"
37 | else
38 | return "unknown"
39 | end
40 | end
41 |
42 | @log.warn "Unable to #{action}: #{e.message}"
43 | raise e
44 | end
45 | end
46 | end
47 |
48 | private
49 |
50 | def xnctl_path path
51 | xnctl = File.join(path, "bin", "launcher")
52 | xnctl = "/bin/sh #{xnctl}" unless FileTest.executable? xnctl
53 | xnctl
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/galaxy/temp.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 | require 'thread'
3 | require 'tmpdir'
4 |
5 | module Galaxy
6 | module Temp
7 | Mutex = Mutex.new
8 |
9 | def Temp.auto_delete path
10 | Kernel.at_exit do
11 | begin
12 | FileUtils.rm_r(path) if File.exist? path
13 | rescue => e
14 | puts "Failed to delete #{path}: #{e}"
15 | end
16 | end
17 | path
18 | end
19 |
20 | def Temp.mk_auto_file component="galaxy"
21 | auto_delete mk_file(component)
22 | end
23 |
24 | def Temp.mk_auto_dir component="galaxy"
25 | auto_delete mk_dir(component)
26 | end
27 |
28 | def Temp.mk_file component="galaxy"
29 | return * FileUtils.touch(next_name(component))
30 | end
31 |
32 | def Temp.mk_dir component="galaxy"
33 | return * FileUtils.mkdir(next_name(component))
34 | end
35 |
36 | private
37 |
38 | def Temp.next_name component
39 | Mutex.synchronize do
40 | @@id ||= 0
41 | name = "";
42 | loop do
43 | @@id += 1
44 | name = File.join Dir::tmpdir, "#{component}.#{Process.pid}.#{@@id}"
45 | return name unless File.exists? name
46 | end
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/lib/galaxy/transport.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | class Transport
3 | @@transports = []
4 |
5 | def self.register transport
6 | @@transports << transport
7 | end
8 |
9 | def self.locate url, log=nil
10 | handler_for(url).locate url, log
11 | end
12 |
13 | def self.publish url, object, log=nil
14 | handler_for(url).publish url, object, log
15 | end
16 |
17 | def self.unpublish url
18 | handler_for(url).unpublish url
19 | end
20 |
21 | def self.handler_for url
22 | @@transports.select { |t| t.can_handle? url }.first or raise "No handler found for #{url}"
23 | end
24 |
25 | def initialize pattern
26 | @pattern = pattern
27 | end
28 |
29 | def can_handle? url
30 | @pattern =~ url
31 | end
32 |
33 | def self.join url
34 | handler_for(url).join url
35 | end
36 | end
37 |
38 | class DRbTransport < Transport
39 | require 'drb'
40 |
41 | def initialize
42 | super(/^druby:.*/)
43 | @servers = {}
44 | end
45 |
46 | def locate url, log=nil
47 | DRbObject.new_with_uri url
48 | end
49 |
50 | def publish url, object, log=nil
51 | @servers[url] = DRb.start_service url, object
52 | end
53 |
54 | def unpublish url
55 | @servers[url].stop_service
56 | @servers[url] = nil
57 | end
58 |
59 | def join url
60 | @servers[url].thread.join
61 | end
62 | end
63 |
64 | class LocalTransport < Transport
65 | def initialize
66 | super(/^local:/)
67 | @servers = {}
68 | end
69 |
70 | def locate url, log=nil
71 | @servers[url]
72 | end
73 |
74 | def publish url, object, log=nil
75 | @servers[url] = object
76 | end
77 |
78 | def unpublish url
79 | @servers[url] = nil
80 | end
81 |
82 | def join url
83 | raise "Not yet implemented"
84 | end
85 | end
86 |
87 | # This http transport isn't used in Galaxy 2.4, which uses http only for anonucements. However, this code shows
88 | # how announcements could be merged via transport. The unit test for this class shows one-direction communication
89 | # (eg, for announcements). To do two way, servers (eg, locate()) would be needed on both sides.
90 | # Note that the console code assumes that the transport initialize blocks, so the calling code (eg console) waits
91 | # for an explicit 'join'. But the Announcer class used here starts a server without blocking and returns immediately.
92 | # Therefore, explicit join is not necessary. So to use, make the console work like the agent: track the main polling
93 | # thread started in initialize() and kill/join when done.
94 | #
95 | class HttpTransport < Transport
96 | require 'galaxy/announcements'
97 |
98 | def initialize
99 | super(/^http:.*/)
100 | @servers = {}
101 | @log = nil
102 | end
103 |
104 | # get object (ie announce fn)
105 | # - install announce() callback
106 | def locate url, log=nil
107 | #DRbObject.new_with_uri url
108 | HTTPAnnouncementSender.new url, log
109 | end
110 |
111 | # make object available (ie console)
112 | def publish url, obj, log=nil
113 | if !obj.respond_to?('process_get') || !obj.respond_to?('process_post')
114 | raise TypeError.new("#{obj.class.name} doesn't contain 'process_post' and 'process_get' methods")
115 | end
116 | return @servers[url] if @servers[url]
117 | begin
118 | @servers[url] = Galaxy::HTTPServer.new(url, obj)
119 | rescue NameError
120 | raise NameError.new("Unable to create the http server. Is mongrel installed?")
121 | end
122 | return @servers[url]
123 | end
124 |
125 | def unpublish url
126 | @servers[url].shutdown
127 | @servers[url] = nil
128 | end
129 |
130 | def join url
131 | #nop
132 | end
133 | end
134 | end
135 |
136 | Galaxy::Transport.register Galaxy::DRbTransport.new
137 | Galaxy::Transport.register Galaxy::LocalTransport.new
138 | Galaxy::Transport.register Galaxy::HttpTransport.new
139 |
140 | # Disable DRb persistent connections (monkey patch)
141 | module DRb
142 | class DRbConn
143 | remove_const :POOL_SIZE
144 | POOL_SIZE = 0
145 | end
146 | end
147 |
--------------------------------------------------------------------------------
/lib/galaxy/version.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | # Don't forget to also update the version in build/sun/pkginfo
3 | Version = "3.0.0"
4 | end
5 |
--------------------------------------------------------------------------------
/lib/galaxy/versioning.rb:
--------------------------------------------------------------------------------
1 | module Galaxy
2 | module Versioning
3 | class StrictVersioningPolicy
4 | def self.assignment_allowed? current_config, requested_config
5 | if current_config.environment == requested_config.environment and current_config.type == requested_config.type
6 | return current_config.version != requested_config.version
7 | end
8 | true
9 | end
10 | end
11 |
12 | class RelaxedVersioningPolicy
13 | def self.assignment_allowed? current_config, requested_config
14 | true
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | com.ning
6 | galaxy
7 | pom
8 | 3.0.0-SNAPSHOT
9 | galaxy
10 | Galaxy, a lightweight software deployment and management tool
11 | http://github.com/ning/galaxy
12 |
13 |
14 | Apache License 2.0
15 | http://www.apache.org/licenses/LICENSE-2.0.html
16 | repo
17 |
18 |
19 |
20 | scm:git:git://github.com/ning/galaxy.git
21 | scm:git:git://github.com/ning/galaxy.git
22 | http://github.com/ning/galaxy/tree/master
23 |
24 |
25 | UTF-8
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-assembly-plugin
32 |
33 |
34 | Create Galaxy distribution
35 |
36 | single
37 |
38 | package
39 |
40 |
41 | assembly.xml
42 |
43 |
44 |
45 |
46 |
47 |
48 | org.apache.maven.plugins
49 | maven-release-plugin
50 | 2.0-beta-9
51 |
52 | forked-path
53 |
54 |
55 |
56 |
57 |
58 |
59 | pierre
60 | Pierre-Alexandre Meyer
61 | pierre@mouraf.org
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/test/bad_core_package/bin/control:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'optparse'
4 |
5 | rest = OptionParser.new do |opts|
6 | opts.on("--base BASE") { |arg| @core_base = arg }
7 | opts.on("--config-path PATH") { |arg| @config_path = arg }
8 | opts.on("--repository URL") { |arg| @repository_base = arg }
9 | end.parse! ARGV
10 |
11 | command = rest.shift
12 |
13 | case command
14 | when 'test-success'
15 | exit! 0
16 | when 'test-failure'
17 | exit! 1
18 | when 'test-arguments'
19 | if (@core_base and
20 | File.join(@core_base, 'bin', 'control') == File.expand_path(__FILE__) and
21 | @config_path == '/test/config/path' and
22 | @repository_base == 'http://repository/base')
23 | exit! 0
24 | else
25 | exit! 1
26 | end
27 | else
28 | exit! 2
29 | end
30 |
--------------------------------------------------------------------------------
/test/bad_core_package/bin/launcher:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'fileutils'
4 |
5 | File.open("/tmp/galaxy_test_#{Process.ppid}", "w") do |f|
6 | f.puts ARGV[0]
7 | end
--------------------------------------------------------------------------------
/test/bad_core_package/bin/xndeploy:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'optparse'
4 | require 'fileutils'
5 |
6 | raise "obviously a major malfunction"
7 |
--------------------------------------------------------------------------------
/test/bad_core_package/stuff:
--------------------------------------------------------------------------------
1 | This is some stuff
2 |
--------------------------------------------------------------------------------
/test/core_package/bin/control:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'optparse'
4 |
5 | rest = OptionParser.new do |opts|
6 | opts.on("--base BASE") { |arg| @core_base = arg }
7 | opts.on("--binaries BINARIES") { |arg| @binaries_base = arg }
8 | opts.on("--config-path PATH") { |arg| @config_path = arg }
9 | opts.on("--repository URL") { |arg| @repository_base = arg }
10 | end.parse! ARGV
11 |
12 | command = rest.shift
13 |
14 | case command
15 | when 'test-success'
16 | puts "gorple"
17 | exit 0
18 | when 'test-failure'
19 | STDERR.puts "fmep"
20 | exit 1
21 | when 'test-multiline'
22 | puts <<-EOM
23 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sit amet arcu a risus pulvinar facilisis.
24 | Proin sed sapien nec magna mattis blandit. Phasellus porta hendrerit eros. Vestibulum ante ipsum primis in
25 | faucibus orci luctus et ultrices posuere cubilia Curae; Integer consequat, ante vitae tempus consequat, nisi
26 | purus facilisis orci, et euismod ligula purus quis magna. Vestibulum diam ante, vestibulum non, adipiscing mollis,
27 | eleifend sed, neque. Cras magna. Fusce non felis et libero posuere facilisis. Cras porttitor tempor orci.
28 | Suspendisse placerat, tortor vel vehicula tempor, felis lorem tincidunt enim, eget cursus lorem tellus non mi.
29 | Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum vitae
30 | risus. Praesent rutrum lectus quis dolor. Aliquam arcu. Sed vulputate mauris.
31 | EOM
32 | exit 0
33 | when 'test-arguments'
34 | if (@core_base and
35 | File.join(@core_base, 'bin', 'control') == File.expand_path(__FILE__) and
36 | @config_path == '/config/path' and
37 | @repository_base == 'http://repository/base' and
38 | @binaries_base == 'http://binaries/base')
39 | exit 0
40 | else
41 | exit 1
42 | end
43 | else
44 | exit 2
45 | end
46 |
--------------------------------------------------------------------------------
/test/core_package/bin/launcher:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'fileutils'
4 |
5 | File.open("/tmp/galaxy_test_#{Process.ppid}", "w") do |f|
6 | f.puts ARGV[0]
7 | end
--------------------------------------------------------------------------------
/test/core_package/bin/xndeploy:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'optparse'
4 | require 'fileutils'
5 |
6 | OptionParser.new do |opts|
7 | opts.on("--base BASE") { |arg| DeployBase = arg }
8 | opts.on("--binaries BINARIES") { |arg| BinariesBase = arg }
9 | opts.on("--config-path PATH") { |arg| ConfigPath = arg }
10 | opts.on("--repository URL") { |arg| Repository = arg }
11 | end.parse! ARGV
12 |
13 | # for test_xndeploy_invoked_on_deploy
14 | begin
15 | dump = {
16 | :deploy_base => DeployBase,
17 | :config_path => ConfigPath,
18 | :repository => Repository,
19 | :binaries_base => BinariesBase,
20 | }
21 | File.open(File.join(DeployBase, "xndeploy_touched_me"), "w") do |file|
22 | Marshal.dump(dump, file)
23 | end
24 | rescue TypeError
25 | end
26 |
--------------------------------------------------------------------------------
/test/core_package/stuff:
--------------------------------------------------------------------------------
1 | This is some stuff
2 |
--------------------------------------------------------------------------------
/test/helper.rb:
--------------------------------------------------------------------------------
1 | require 'tempfile'
2 | require 'fileutils'
3 |
4 | require 'galaxy/filter'
5 | require 'galaxy/temp'
6 | require 'galaxy/transport'
7 | require 'galaxy/version'
8 | require 'galaxy/versioning'
9 |
10 | module Helper
11 |
12 | def Helper.mk_tmpdir
13 | Galaxy::Temp.mk_auto_dir "testing"
14 | end
15 |
16 | class Mock
17 |
18 | def initialize listeners={}
19 | @listeners = listeners
20 | end
21 |
22 | def method_missing sym, *args
23 | f = @listeners[sym]
24 | if f
25 | f.call(*args)
26 | end
27 | end
28 | end
29 |
30 | end
31 |
32 | class MockConsole
33 | def initialize agents
34 | @agents = agents
35 | end
36 |
37 | def shutdown
38 | end
39 |
40 | def agents filters = { :set => :all }
41 | filter = Galaxy::Filter.new filters
42 | @agents.select(&filter)
43 | end
44 | end
45 |
46 | class MockAgent
47 | attr_reader :host, :config_path, :stopped, :started, :restarted
48 | attr_reader :gonsole_url, :env, :version, :type, :url, :agent_status, :proxy, :build, :core_type, :machine, :ip
49 |
50 | def initialize host, env = nil, version = nil, type = nil, gonsole_url=nil
51 | @host = host
52 | @env = env
53 | @version = version
54 | @type = type
55 | @gonsole_url = gonsole_url
56 | @stopped = @started = @restarted = false
57 |
58 | @url = "local://#{host}"
59 | Galaxy::Transport.publish @url, self
60 |
61 | @config_path = nil
62 | @config_path = "/#{env}/#{version}/#{type}" unless env.nil? || version.nil? || type.nil?
63 | @agent_status = 'online'
64 | @status = 'online'
65 | @proxy = Galaxy::Transport.locate(@url)
66 | @build = "1.2.3"
67 | @core_type = 'test'
68 |
69 | @ip = nil
70 | @drb_url = nil
71 | @os = nil
72 | @machine = nil
73 | end
74 |
75 | def shutdown
76 | Galaxy::Transport.unpublish @url
77 | end
78 |
79 | def status
80 | OpenStruct.new(
81 | :host => @host,
82 | :ip => @ip,
83 | :url => @drb_url,
84 | :os => @os,
85 | :machine => @machine,
86 | :core_type => @core_type,
87 | :config_path => @config_path,
88 | :build => @build,
89 | :status => @status,
90 | :agent_status => 'online',
91 | :galaxy_version => Galaxy::Version
92 | )
93 | end
94 |
95 | def stop!
96 | @stopped = true
97 | status
98 | end
99 |
100 | def start!
101 | @started = true
102 | status
103 | end
104 |
105 | def restart!
106 | @restarted = true
107 | status
108 | end
109 |
110 | def become! path, versioning_policy = Galaxy::Versioning::StrictVersioningPolicy
111 | md = %r!^/([^/]+)/([^/]+)/(.*)$!.match path
112 | new_env, new_version, new_type = md[1], md[2], md[3]
113 | # XXX We don't test the versioning code - but it should go away soon
114 | #raise if @version == new_version
115 | @env = new_env
116 | @version = new_version
117 | @type = new_type
118 | @config_path = "/#{@env}/#{@version}/#{@type}"
119 | status
120 | end
121 |
122 | def update_config! new_version, versioning_policy = Galaxy::Versioning::StrictVersioningPolicy
123 | # XXX We don't test the versioning code - but it should go away soon
124 | #raise if @version == new_version
125 | @version = new_version
126 | @config_path = "/#{@env}/#{@version}/#{@type}"
127 | status
128 | end
129 |
130 | def check_credentials!(command, credentials)
131 | true
132 | end
133 |
134 | def inspect
135 | Galaxy::Client::SoftwareDeploymentReport.new.record_result(self)
136 | end
137 | end
138 |
--------------------------------------------------------------------------------
/test/performance/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/test/performance/lib/commons-logging-1.1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pierre/galaxy/0113bd79785b139db3565e416340e3ccbe22a3ae/test/performance/lib/commons-logging-1.1.1.jar
--------------------------------------------------------------------------------
/test/performance/lib/httpclient-4.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pierre/galaxy/0113bd79785b139db3565e416340e3ccbe22a3ae/test/performance/lib/httpclient-4.0.1.jar
--------------------------------------------------------------------------------
/test/performance/lib/httpcore-4.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pierre/galaxy/0113bd79785b139db3565e416340e3ccbe22a3ae/test/performance/lib/httpcore-4.0.1.jar
--------------------------------------------------------------------------------
/test/performance/lib/httpmime-4.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pierre/galaxy/0113bd79785b139db3565e416340e3ccbe22a3ae/test/performance/lib/httpmime-4.0.1.jar
--------------------------------------------------------------------------------
/test/performance/lib/one-jar-ant-task-0.96.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pierre/galaxy/0113bd79785b139db3565e416340e3ccbe22a3ae/test/performance/lib/one-jar-ant-task-0.96.jar
--------------------------------------------------------------------------------
/test/performance/src/LoadTest.java:
--------------------------------------------------------------------------------
1 | import java.text.MessageFormat;
2 | import java.util.concurrent.BlockingQueue;
3 | import java.util.concurrent.LinkedBlockingQueue;
4 |
5 | import org.apache.http.HttpVersion;
6 | import org.apache.http.client.HttpClient;
7 | import org.apache.http.client.methods.HttpPost;
8 | import org.apache.http.entity.StringEntity;
9 | import org.apache.http.impl.client.DefaultHttpClient;
10 | import org.apache.http.params.BasicHttpParams;
11 | import org.apache.http.params.HttpConnectionParams;
12 | import org.apache.http.params.HttpParams;
13 | import org.apache.http.params.HttpProtocolParams;
14 |
15 | public class LoadTest {
16 | static BlockingQueue queue = new LinkedBlockingQueue();
17 |
18 | static Thread createWorker(int workerId, final int agentId, final int totalRequests, final String url) {
19 | final MessageFormat messageFormat = new MessageFormat(
20 | "--- !ruby/object:OpenStruct\ntable:\n "
21 | + ":agent_status: online\n " + ":config_path: a/b/c\n "
22 | + ":host: z{0}.company.com\n "
23 | + ":url: druby://z{1}.company.com:4441\n "
24 | + ":core_type: benchmark\n " + ":eventType: galaxy\n "
25 | + ":machine: m{2}.company.com\n " + ":galaxy_version: 2.6.4\n "
26 | + ":os: linux\n " + ":galaxy_event_type: success");
27 |
28 | final String name = "Worker " + workerId;
29 | Thread thread = new Thread(name) {
30 | @Override
31 | public void run() {
32 | boolean success;
33 | for (int i = 0, j; i < totalRequests; i++) {
34 | success = false;
35 | try {
36 | // System.err.println(this.getName() + " : #" + (i + 1));
37 | System.out.print(".");
38 | HttpParams httpParams = new BasicHttpParams();
39 | HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
40 | HttpConnectionParams.setSoTimeout(httpParams, new Integer(30000));
41 | HttpConnectionParams.setConnectionTimeout(httpParams, new Integer(30000));
42 | HttpClient httpClient = new DefaultHttpClient(httpParams);
43 | HttpPost httpPost = new HttpPost(url);
44 | j = agentId + i;
45 | Object[] input = new Object[] { j , j, j };
46 | StringEntity entity = new StringEntity(messageFormat.format(input));
47 | httpPost.setEntity(entity);
48 | httpClient.execute(httpPost);
49 | httpClient.getConnectionManager().shutdown();
50 | success = true;
51 | } catch (Exception e) {
52 | // System.err.println(e.getLocalizedMessage());
53 | }
54 | finally {
55 | if (!success) {
56 | String msg = name + " #" + (i + 1);
57 | // System.err.println("retrying worker " + msg);
58 | try {
59 | queue.put(msg);
60 | } catch (InterruptedException e) {
61 | e.printStackTrace();
62 | }
63 | }
64 | }
65 | }
66 | }
67 | };
68 | return thread;
69 | }
70 |
71 | public static void main(String[] args) throws Exception {
72 | int agents = 1400;
73 | int concurrentRequests = 70;
74 | String gonsoleUrl = "http://localhost:4442";
75 | if (args.length == 1 && args[0].endsWith("-h")) {
76 | System.out.println("java -jar galaxy-loader.jar -g -a <# of agents> -c <# concurrent requests>");
77 | System.exit(0);
78 | }
79 | for (int i = 1; i < args.length; i += 2) {
80 | if("-g".equals(args[i - 1].trim())) {
81 | gonsoleUrl = args[i].trim();
82 | }
83 | else if("-a".equals(args[i - 1].trim())) {
84 | agents = Integer.parseInt(args[i].trim());
85 | }
86 | else if("-c".equals(args[i - 1].trim())) {
87 | concurrentRequests = Integer.parseInt(args[i].trim());
88 | }
89 | }
90 |
91 | System.out.println("gonsole = " + gonsoleUrl + ", agents = " + agents + ", concurrent requests = " + concurrentRequests);
92 | int totalWorkers = concurrentRequests;
93 | int totalRequests = (int) Math.round(agents / (double) concurrentRequests);
94 | Thread[] workers = new Thread[totalWorkers];
95 | for (int i = 0, j; i < totalWorkers; i++) {
96 | j = (i + 1) * totalRequests;
97 | workers[i] = createWorker((i + 1), j, totalRequests, gonsoleUrl);
98 | }
99 |
100 | long time = System.currentTimeMillis();
101 | for (Thread thread : workers) {
102 | thread.start();
103 | }
104 |
105 | for (Thread thread : workers) {
106 | try {
107 | thread.join();
108 | } catch (InterruptedException e) {
109 | }
110 | }
111 | time = System.currentTimeMillis() - time;
112 | // for (String msg : queue) {
113 | // System.out.println(msg);
114 | // }
115 | System.out.println("\ntotal failures: " + queue.size());
116 | System.out.println("total run time: " + (time / (1000)) + " sec");
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/test/property_data/a/b/c/d/test_override.properties:
--------------------------------------------------------------------------------
1 | oscar = purple
2 |
--------------------------------------------------------------------------------
/test/property_data/a/b/c/test_simple.properties:
--------------------------------------------------------------------------------
1 | chris = green
2 |
--------------------------------------------------------------------------------
/test/property_data/a/b/test_override.properties:
--------------------------------------------------------------------------------
1 | oscar = blue
2 | sam = red
--------------------------------------------------------------------------------
/test/property_data/a/b/xncore.properties:
--------------------------------------------------------------------------------
1 | oscar = green
2 | core = hello
3 |
--------------------------------------------------------------------------------
/test/property_data/a/build.properties:
--------------------------------------------------------------------------------
1 | type=test
2 | build=1.0-12345
3 |
--------------------------------------------------------------------------------
/test/property_data/a/test_comments_ignored.properties:
--------------------------------------------------------------------------------
1 | #hello = 7
2 | red = fuschia
--------------------------------------------------------------------------------
/test/property_data/foo-bar.properties:
--------------------------------------------------------------------------------
1 | helo=g'bye
--------------------------------------------------------------------------------
/test/test_agent.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/transport'
6 | require 'galaxy/agent'
7 | require 'galaxy/host'
8 | require 'webrick'
9 | require 'thread'
10 | require 'timeout'
11 | require 'helper'
12 | require 'fileutils'
13 | require 'logger'
14 |
15 | class TestAgent < Test::Unit::TestCase
16 |
17 | def setup
18 | @tempdir = Helper.mk_tmpdir
19 |
20 | @data_dir = File.join(@tempdir, 'data')
21 | @deploy_dir = File.join(@tempdir, 'deploy')
22 | @binaries_base = File.join(@tempdir, 'binaries')
23 |
24 | FileUtils.mkdir_p @data_dir
25 | FileUtils.mkdir_p @deploy_dir
26 | FileUtils.mkdir_p @binaries_base
27 |
28 | system "#{Galaxy::HostUtils.tar} -C #{File.join(File.dirname(__FILE__), "core_package")} -czf #{@binaries_base}/test-1.0-12345.tar.gz ."
29 |
30 | webrick_logger = Logger.new(STDOUT)
31 | webrick_logger.level = Logger::WARN
32 | @server = WEBrick::HTTPServer.new(:Port => 8000, :Logger => webrick_logger)
33 |
34 | # Replies on POST from agent
35 | @server.mount_proc("/") do |request, response|
36 | status, content_type, body = 200, "text/plain", "pong"
37 | response.status = status
38 | response['Content-Type'] = content_type
39 | response.body = body
40 | end
41 | @server.mount("/config", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__), "property_data"), true)
42 | @server.mount("/binaries", WEBrick::HTTPServlet::FileHandler, @binaries_base, true)
43 |
44 | Thread.start do
45 | @server.start
46 | end
47 |
48 | # Note: force 127.0.0.1 not to rely on `hostname` and localhost
49 | @agent = Galaxy::Agent.start({:repository => File.dirname(__FILE__) + "/property_data",
50 | :binaries => @binaries_base,
51 | :data_dir => @data_dir,
52 | :deploy_dir => @deploy_dir,
53 | :log_level => Logger::WARN,
54 | :host => "druby://127.0.0.1:4441",
55 | :console => "http://127.0.0.1:8000"
56 | })
57 | end
58 |
59 | def teardown
60 | @agent.shutdown
61 | @server.shutdown
62 | FileUtils.rm_rf @tempdir
63 | end
64 |
65 | def test_agent_assign
66 | @agent.become! '/a/b/c'
67 | assert File.exist?(File.join(@deploy_dir, 'current', 'bin'))
68 | end
69 |
70 | def test_agent_perform
71 | @agent.become! '/a/b/c'
72 | assert_nothing_raised do
73 | @agent.perform! 'test-success'
74 | end
75 | end
76 |
77 | def test_agent_perform_failure
78 | @agent.become! '/a/b/c'
79 | assert_raise RuntimeError do
80 | @agent.logger.log.level = Logger::FATAL
81 | # The failure will spit a stacktrace in the log (ERROR)
82 | @agent.perform! 'test-failure'
83 | @agent.logger.log.level = Logger::WARN
84 | end
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/test/test_announcements.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/announcements'
6 |
7 | class TestAnnouncements < Test::Unit::TestCase
8 |
9 | # example callback for action upon receiving an announcement
10 | def on_announcement(ann)
11 | assert "bar" == ann.foo # these are not Test::Unit asserts, but $received won't be set if any are false
12 | assert ann.rand >= 0
13 | assert ann.rand < 10
14 | assert "eggs" == ann.item
15 | @@received = true
16 | end
17 |
18 | def test_server
19 | # url = "http://localhost:8000" # 4442 for announcements in production, but can be anything for test
20 | # # server
21 | # Galaxy::HTTPAnnouncementReceiver.new(url, lambda{|a| on_announcement(a)})
22 | #
23 | # # sender
24 | # announcer = HTTPAnnouncementSender.new(url)
25 | # @@received = false
26 | # announcer.announce(OpenStruct.new(:foo=>"bar", :rand => rand(10), :item => "eggs"))
27 | # assert_equal true, @@received
28 | end
29 | end
--------------------------------------------------------------------------------
/test/test_client.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/agent'
6 | require 'galaxy/command'
7 | require 'galaxy/console'
8 | require 'logger'
9 |
10 | class TestClient < Test::Unit::TestCase
11 |
12 | ENV.delete 'GALAXY_CONSOLE'
13 | GALAXY = "ruby -Ilib bin/galaxy"
14 |
15 | Galaxy::Commands.each do |command|
16 | define_method "test_#{command}_usage" do
17 | output = `#{GALAXY} -h #{command} 2>&1`
18 | assert_match("Usage for '#{command}':", output)
19 | end
20 | end
21 |
22 | def test_usage_with_no_arguments
23 | output = `#{GALAXY} 2>&1`
24 | assert_match("Error: Missing command", output)
25 | end
26 |
27 | def test_show_with_no_console
28 | output = `#{GALAXY} show 2>&1`
29 | assert_match("Error: Cannot determine console host", output)
30 | end
31 |
32 | def test_show_console
33 | console = Galaxy::Console.start({ :host => 'localhost' })
34 | output = `#{GALAXY} show-console -c localhost 2>&1`
35 | assert_match("druby://localhost:4440\thttp://localhost:4442\tlocalhost\t-\t5\n", output)
36 | console.shutdown
37 | end
38 |
39 | def test_show_with_console_from_environment
40 | # console = Galaxy::Console.start({ :host => 'localhost' })
41 | # output = `GALAXY_CONSOLE=localhost #{GALAXY} show 2>&1`
42 | # assert_match("No agents matching the provided filter(s) were available for show", output)
43 | # console.shutdown
44 | end
45 |
46 | def test_show_with_console_from_command_line
47 | # console = Galaxy::Console.start({ :host => 'localhost' })
48 | # output = `#{GALAXY} -c localhost show 2>&1`
49 | # assert_match("No agents matching the provided filter(s) were available for show", output)
50 | # console.shutdown
51 | end
52 |
53 | def test_show_with_bad_console
54 | output = `#{GALAXY} -c non-existent-host show 2>&1`
55 | # On Linux, this will be:
56 | # Error: druby://non-existent-host:4440 - #"
57 | #assert_match("Error: druby://non-existent-host:4440 - # 'localhost', :log_level => Logger::WARN })
63 | agent = Galaxy::Agent.start({ :host => 'localhost', :console => 'localhost', :log_level => Logger::WARN })
64 | output = `#{GALAXY} -c localhost show 2>&1`.split("\n")
65 | assert_equal(1, output.length)
66 | assert_match("No agents matching the provided filter(s) were available for show", output[0])
67 | agent.shutdown
68 | console.shutdown
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/test/test_commands.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/command'
6 | require 'galaxy/transport'
7 | require 'helper'
8 |
9 | class TestCommands < Test::Unit::TestCase
10 | def setup
11 | @agents = [
12 | MockAgent.new("agent1", "alpha", "1.0", "sysc"),
13 | MockAgent.new("agent2", "alpha", "1.0", "idtc"),
14 | MockAgent.new("agent3", "alpha", "1.0", "appc/aclu0"),
15 | MockAgent.new("agent4"),
16 | MockAgent.new("agent5", "alpha", "2.0", "sysc"),
17 | MockAgent.new("agent6", "beta", "1.0", "sysc"),
18 | MockAgent.new("agent7")
19 | ]
20 |
21 | @console = MockConsole.new(@agents)
22 | end
23 |
24 | def teardown
25 | @agents.each { |a| a.shutdown }
26 | @console.shutdown
27 | end
28 |
29 | def test_all_registered
30 | assert Galaxy::Commands["assign"]
31 | assert Galaxy::Commands["clear"]
32 | assert Galaxy::Commands["reap"]
33 | assert Galaxy::Commands["restart"]
34 | assert Galaxy::Commands["rollback"]
35 | assert Galaxy::Commands["show"]
36 | assert Galaxy::Commands["ssh"]
37 | assert Galaxy::Commands["start"]
38 | assert Galaxy::Commands["stop"]
39 | assert Galaxy::Commands["update"]
40 | assert Galaxy::Commands["update-config"]
41 | end
42 |
43 | def internal_test_all_for cmd
44 | command = Galaxy::Commands[cmd].new [], {:console => @console}
45 | agents = command.select_agents(:set => :all)
46 | command.execute agents
47 |
48 | @agents.select { |a| a.config_path }.each { |a| assert_equal true, yield(a) }
49 | @agents.select { |a| a.config_path.nil? }.each { |a| assert_equal false, yield(a) }
50 | end
51 |
52 | def internal_test_by_host cmd
53 | command = Galaxy::Commands[cmd].new [], {:console => @console}
54 | agents = command.select_agents(:host => "agent1")
55 | command.execute agents
56 |
57 | @agents.select {|a| a.host == "agent1" }.each { |a| assert_equal true, yield(a) }
58 | @agents.select {|a| a.host != "agent1" }.each { |a| assert_equal false, yield(a) }
59 | end
60 |
61 | def internal_test_by_type cmd
62 | command = Galaxy::Commands[cmd].new [], {:console => @console}
63 | agents = command.select_agents(:type => "sysc")
64 | command.execute agents
65 |
66 | @agents.select {|a| a.type == "sysc" }.each { |a| assert_equal true, yield(a) }
67 | @agents.select {|a| a.type != "sysc" }.each { |a| assert_equal false, yield(a) }
68 | end
69 |
70 | def test_stop_all
71 | internal_test_all_for("stop") { |a| a.stopped }
72 | end
73 |
74 | def test_start_all
75 | internal_test_all_for("start") { |a| a.started }
76 | end
77 |
78 | def test_restart_all
79 | internal_test_all_for("restart") { |a| a.restarted }
80 | end
81 |
82 | def test_stop_by_host
83 | internal_test_by_host("stop") { |a| a.stopped }
84 | end
85 |
86 | def test_start_by_host
87 | internal_test_by_host("start") { |a| a.started }
88 | end
89 |
90 | def test_restart_by_host
91 | internal_test_by_host("restart") { |a| a.restarted }
92 | end
93 |
94 | def test_stop_by_type
95 | internal_test_by_type("stop") { |a| a.stopped }
96 | end
97 |
98 | def test_start_by_type
99 | internal_test_by_type("start") { |a| a.started }
100 | end
101 |
102 | def test_restart_by_type
103 | internal_test_by_type("restart") { |a| a.restarted }
104 | end
105 |
106 | def test_show_all
107 | command = Galaxy::Commands["show"].new [], {:console => @console}
108 | agents = command.select_agents(:set => :all)
109 | results = command.execute agents
110 |
111 | assert_equal format_agents, results
112 | end
113 |
114 | def test_show_by_env
115 | command = Galaxy::Commands["show"].new [], {:console => @console}
116 | agents = command.select_agents(:env => "alpha")
117 | results = command.execute agents
118 |
119 | assert_equal format_agents(@agents.select {|a| a.env == "alpha"}), results
120 | end
121 |
122 | def test_show_by_version
123 | command = Galaxy::Commands["show"].new [], {:console => @console, :version => "1.0"}
124 | agents = command.select_agents(:version => "1.0")
125 | results = command.execute agents
126 |
127 | assert_equal format_agents(@agents.select {|a| a.version == "1.0"}), results
128 | end
129 |
130 | def test_show_by_type
131 | command = Galaxy::Commands["show"].new [], {:console => @console}
132 | agents = command.select_agents(:type => :sysc)
133 | results = command.execute agents
134 |
135 | assert_equal format_agents(@agents.select {|a| a.type == "sysc"}), results
136 | end
137 |
138 | def test_show_by_type2
139 | command = Galaxy::Commands["show"].new [], {:console => @console}
140 | agents = command.select_agents(:type => "appc/aclu0")
141 | results = command.execute agents
142 |
143 | assert_equal format_agents(@agents.select {|a| a.type == "appc/aclu0"}), results
144 | end
145 |
146 | def test_show_by_env_version_type
147 | command = Galaxy::Commands["show"].new [], {:console => @console}
148 | agents = command.select_agents({:type => "sysc", :env => "alpha", :version => "1.0"})
149 | results = command.execute agents
150 |
151 | assert_equal format_agents(@agents.select {|a| a.type == "sysc" && a.env == "alpha" && a.version == "1.0"}), results
152 | end
153 |
154 | def test_assign_empty
155 | command = Galaxy::Commands["assign"].new ["beta", "3.0", "rslv"], {:console => @console, :set => :empty}
156 | agents = command.select_agents(:set => :all)
157 | agent = @agents.select { |a| a.config_path.nil? }.first
158 | command.execute agents
159 | assert_equal "beta", agent.env
160 | assert_equal "rslv", agent.type
161 | assert_equal "3.0", agent.version
162 | end
163 |
164 | def test_assign_by_host
165 | agent = @agents.select { |a| a.host == "agent7" }.first
166 |
167 | command = Galaxy::Commands["assign"].new ["beta", "3.0", "rslv"], { :console => @console }
168 | agents = command.select_agents(:host => agent.host)
169 | command.execute agents
170 |
171 | assert_equal "beta", agent.env
172 | assert_equal "rslv", agent.type
173 | assert_equal "3.0", agent.version
174 | end
175 |
176 | def test_clear
177 | # TODO
178 | end
179 |
180 | def test_clear_by_host
181 | # TODO
182 | end
183 |
184 | def test_update_by_host
185 | agent = @agents.select { |a| !a.config_path.nil? }.first
186 | env = agent.env
187 | type = agent.type
188 |
189 | command = Galaxy::Commands["update"].new ["4.0"], { :console => @console }
190 | agents = command.select_agents(:host => agent.host)
191 | command.execute agents
192 |
193 | assert_equal env, agent.env
194 | assert_equal type, agent.type
195 | assert_equal "4.0", agent.version
196 | end
197 |
198 | def test_update_config_by_host
199 | agent = @agents.select { |a| !a.config_path.nil? }.first
200 | env = agent.env
201 | type = agent.type
202 |
203 | command = Galaxy::Commands["update-config"].new ["4.0"], { :console => @console }
204 | agents = command.select_agents(:version => "1.0")
205 | results = command.execute agents
206 | assert_equal env, agent.env
207 | assert_equal type, agent.type
208 | assert_equal "4.0", agent.version
209 | end
210 |
211 | private
212 |
213 | def format_agents(agents=@agents)
214 | res = agents.inject("") do |memo, a|
215 | memo.empty? ? a.inspect : memo.to_s + a.inspect
216 | end
217 | res
218 | end
219 | end
220 |
--------------------------------------------------------------------------------
/test/test_config.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/config'
6 | require 'galaxy/log'
7 | require 'helper'
8 | require 'ostruct'
9 | require 'stringio'
10 | require 'logger'
11 |
12 | class TestConfig < Test::Unit::TestCase
13 |
14 | def setup
15 | @s = OpenStruct.new
16 | @c = Galaxy::AgentConfigurator.new @s
17 | @c2 = Galaxy::ConsoleConfigurator.new @s
18 | end
19 |
20 | def test_host_defaults_to_hostname
21 | assert_equal `hostname`.strip, @c.host
22 | end
23 |
24 | def test_logging
25 | Galaxy::HostUtils.logger("fred").info "boo!"
26 | Galaxy::HostUtils.logger("fred").info "warn!"
27 | end
28 |
29 | def test_data_dir
30 | assert_equal "#{Galaxy::HostUtils.avail_path}/galaxy-agent/data" , @c.data_dir
31 | end
32 |
33 | def test_deploy_dir
34 | assert_equal "#{Galaxy::HostUtils.avail_path}/galaxy-agent/deploy" , @c.deploy_dir
35 | end
36 |
37 | def test_deploy_dir_specced
38 | @s.deploy_dir = "/tmp/plop"
39 | assert_equal "/tmp/plop", @c.deploy_dir
40 | end
41 |
42 | def test_data_dir_specced
43 | @s.data_dir = "/tmp/plop"
44 | assert_equal "/tmp/plop", @c.data_dir
45 | end
46 |
47 | def test_verbose
48 | @s.verbose = true
49 | assert @c.configure[:verbose]
50 | end
51 |
52 | def test_log_level_debug
53 | @s.log_level = "DEBUG"
54 | assert_equal Logger::DEBUG, @c.configure[:log_level]
55 | end
56 |
57 | def test_log_level_info
58 | @s.log_level = "INFO"
59 | assert_equal Logger::INFO, @c.configure[:log_level]
60 | end
61 |
62 | def test_log_level_warn
63 | @s.log_level = "WARN"
64 | assert_equal Logger::WARN, @c.configure[:log_level]
65 | end
66 |
67 | def test_log_level_error
68 | @s.log_level = "ERROR"
69 | assert_equal Logger::ERROR, @c.configure[:log_level]
70 | end
71 |
72 | def test_log_level_other
73 | @s.log_level = "---UNK"
74 | assert_equal nil, @c.configure[:log_level]
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/test/test_console.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/transport'
6 | require 'galaxy/config'
7 | require 'galaxy/console'
8 | require 'thread'
9 | require 'timeout'
10 | require 'helper'
11 |
12 | class TestConsole < Test::Unit::TestCase
13 |
14 | def setup
15 | @foo = OpenStruct.new({
16 | :host => 'foo',
17 | :ip => '10.0.0.1',
18 | :machine => 'foomanchu',
19 | :config_path => '/alpha/1.0/bloo',
20 | :status => 'running'
21 | })
22 |
23 | @bar = OpenStruct.new({
24 | :host => 'bar',
25 | :ip => '10.0.0.2',
26 | :machine => 'barmanchu',
27 | :config_path => '/beta/2.0/blar',
28 | :status => 'stopped'
29 | })
30 |
31 | @baz = OpenStruct.new({
32 | :host => 'baz',
33 | :ip => '10.0.0.3',
34 | :machine => 'bazmanchu',
35 | :config_path => '/gamma/3.0/blaz',
36 | :status => 'dead'
37 | })
38 |
39 | @blee = OpenStruct.new({
40 | :host => 'blee',
41 | :ip => '10.0.0.4',
42 | :machine => 'bleemanchu'
43 | })
44 |
45 | @console = Galaxy::Console.start({:host => "localhost", :url => "druby://localhost:4449"})
46 | end
47 |
48 | def teardown
49 | @console.shutdown
50 | end
51 |
52 | def test_updates_last_announced_on_announce
53 | assert_nil @console.db["foo"]
54 |
55 | @console.send("announce", @foo)
56 | first = @console.db["foo"].timestamp
57 | @console.send("announce", @foo)
58 | second = @console.db["foo"].timestamp
59 |
60 | assert second > first
61 | end
62 |
63 | def test_list_agents
64 | @console.send("announce", @foo)
65 | @console.send("announce", @bar)
66 | @console.send("announce", @baz)
67 |
68 | agents = @console.agents
69 | assert_equal 3, agents.length
70 |
71 | assert agents.include?(@foo)
72 | assert agents.include?(@bar)
73 | assert agents.include?(@baz)
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/test/test_controller.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/controller'
6 | require 'galaxy/deployer'
7 | require 'galaxy/host'
8 | require 'helper'
9 | require 'fileutils'
10 | require 'logger'
11 |
12 | class TestController < Test::Unit::TestCase
13 |
14 | def setup
15 | @core_package = Tempfile.new("package.tgz").path
16 | system %{
17 | #{Galaxy::HostUtils.tar} -C #{File.join(File.dirname(__FILE__), "core_package")} -czf #{@core_package} .
18 | }
19 | @path = Helper.mk_tmpdir
20 | @deployer = Galaxy::Deployer.new @path, Logger.new("/dev/null")
21 | @core_base = @deployer.deploy "1", @core_package, "/config", "/repository", "/binaries"
22 | @controller = Galaxy::Controller.new @core_base, '/config/path', 'http://repository/base', 'http://binaries/base', Logger.new("/dev/null")
23 | end
24 |
25 | def test_perform_success
26 | output = @controller.perform!('test-success')
27 | assert_equal "gorple\n", output
28 | end
29 |
30 | def test_perform_failure
31 | assert_raise Galaxy::Controller::CommandFailedException do
32 | @controller.perform!('test-failure')
33 | end
34 | end
35 |
36 | def test_perform_unrecognized
37 | assert_raise Galaxy::Controller::UnrecognizedCommandException do
38 | @controller.perform!('unrecognized')
39 | end
40 | end
41 |
42 | def test_controller_arguments
43 | assert_nothing_raised do
44 | @controller.perform!('test-arguments')
45 | end
46 | end
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/test/test_db.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/db'
6 | require 'helper'
7 |
8 | class TestDB < Test::Unit::TestCase
9 |
10 | def setup
11 | @db = Galaxy::DB.new "#{Helper.mk_tmpdir}/galaxy.db"
12 | end
13 |
14 | def test_silly
15 | @db["k"] = "v"
16 | assert_equal "v", @db["k"]
17 | end
18 |
19 | def test_in_child
20 | @db["name"] = "Fred"
21 | pid = fork do
22 | if @db["name"] == "Fred"
23 | exit 0
24 | else
25 | exit 1
26 | end
27 | end
28 | _, status = Process.waitpid2 pid
29 | assert_equal 0, status.exitstatus
30 | end
31 |
32 |
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/test/test_deployer.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/deployer'
6 | require 'galaxy/host'
7 | require 'helper'
8 | require 'fileutils'
9 | require 'logger'
10 |
11 | class TestDeployer < Test::Unit::TestCase
12 |
13 | def setup
14 | @core_package = Tempfile.new("package.tgz").path
15 | @bad_core_package = Tempfile.new("bad-package.tgz").path
16 | system %{
17 | #{Galaxy::HostUtils.tar} -C #{File.join(File.dirname(__FILE__), "core_package")} -czf #{@core_package} .
18 | }
19 | system %{
20 | #{Galaxy::HostUtils.tar} -C #{File.join(File.dirname(__FILE__), "bad_core_package")} -czf #{@bad_core_package} .
21 | }
22 | @path = Helper.mk_tmpdir
23 | @deployer = Galaxy::Deployer.new @path, Logger.new("/dev/null")
24 | end
25 |
26 | def test_core_base_is_right
27 | core_base = @deployer.deploy "2", @core_package, "/config", "/repository", "/binaries"
28 | assert_equal File.join(@path, "2"), core_base
29 | end
30 |
31 | def test_deployment_dir_is_made
32 | core_base = @deployer.deploy "2", @core_package, "/config", "/repository", "/binaries"
33 | assert FileTest.directory?(core_base)
34 | end
35 |
36 | def test_xndeploy_exists_after_deployment
37 | core_base = @deployer.deploy "2", @core_package, "/config", "/repository", "/binaries"
38 | assert FileTest.exists?(File.join(core_base, "bin", "xndeploy"))
39 | end
40 |
41 | def test_xndeploy_invoked_on_deploy
42 | core_base = @deployer.deploy "2", @core_package, "/config", "/repository", "/binaries"
43 | assert FileTest.exists?(File.join(core_base, "xndeploy_touched_me"))
44 | end
45 |
46 | def test_xndeploy_gets_correct_values
47 | core_base = @deployer.deploy "2", @core_package, "/config", "/repository", "/binaries"
48 | dump = File.open(File.join(core_base, "xndeploy_touched_me")) do |file|
49 | Marshal.load file
50 | end
51 | assert_equal core_base, dump[:deploy_base]
52 | assert_equal "/config", dump[:config_path]
53 | assert_equal "/repository", dump[:repository]
54 | assert_equal "/binaries", dump[:binaries_base]
55 | end
56 |
57 | def test_current_symlink_created
58 | core_base = @deployer.deploy "2", @core_package, "/config", "/repository", "/binaries"
59 | link = File.join(@path, "current")
60 | assert_equal false, FileTest.symlink?(link)
61 | @deployer.activate "2"
62 | assert FileTest.symlink?(link)
63 | assert_equal File.join(@path, "2"), File.readlink(link)
64 | end
65 |
66 | def test_upgrade
67 | first = @deployer.deploy "1", @core_package, "/config", "/repository", "/binaries"
68 | @deployer.activate "1"
69 | assert_equal File.join(@path, "1"), File.readlink(File.join(@path, "current"))
70 |
71 | first = @deployer.deploy "2", @core_package, "/config", "/repository", "/binaries"
72 | @deployer.activate "2"
73 | assert_equal File.join(@path, "2"), File.readlink(File.join(@path, "current"))
74 | end
75 |
76 | def test_bad_archive
77 | assert_raise RuntimeError do
78 | @deployer.deploy "bad", "/etc/hosts", "/config", "/repository", "/binaries"
79 | end
80 | end
81 |
82 | def test_deploy_script_failure
83 | assert_raise RuntimeError do
84 | @deployer.deploy "bad", @bad_core_package, "/config", "/repository", "/binaries"
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/test/test_event.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/events'
6 | require 'logger'
7 |
8 | class TestEvent < Test::Unit::TestCase
9 |
10 | # Set your collector hostname here to run tests.
11 | # See http://github.com/ning/collector
12 | COLLECTOR_HOST = nil
13 |
14 | def test_collectors
15 | unless COLLECTOR_HOST.nil?
16 | send_log
17 | send_raw_event
18 | send_success_event
19 | send_error_event
20 | build_number_string
21 | else
22 | assert true
23 | end
24 | end
25 |
26 | def setup
27 | logger = Logger.new(STDOUT)
28 | logger.level = Logger::WARN
29 |
30 | @galaxy_sender = Galaxy::GalaxyEventSender.new(COLLECTOR_HOST, "http://gonsole.testing.company.net:1242", "127.0.0.1", logger)
31 | @log_sender = Galaxy::GalaxyLogEventSender.new(COLLECTOR_HOST, "http://gonsole.testing.company.net:1242", "127.0.0.1", logger)
32 |
33 | @event = OpenStruct.new(
34 | :host => "prod1.company.com",
35 | :ip => "192.168.12.42",
36 | :url => "drb://goofabr.company.pouet",
37 | :os => "Linux",
38 | :machine => "foobar",
39 | :core_type => "tester",
40 | :config_path => "conf/bar/baz",
41 | :build => "124212",
42 | :status => "running",
43 | :agent_status => "online",
44 | :galaxy_version => "2.5.1",
45 | :user => "John Doe",
46 | :gonsole_url => "http://gonsole.qa.company.net:4442"
47 | )
48 | end
49 |
50 | # More tests in test_logger.rb
51 | def send_log
52 | assert @log_sender.dispatch_error_log("Hello world!", "program_test")
53 | end
54 |
55 | def send_raw_event
56 | assert @galaxy_sender.send_event(@event)
57 | end
58 |
59 | def send_success_event
60 | assert @galaxy_sender.dispatch_announce_success_event(@event)
61 | end
62 |
63 | def send_error_event
64 | assert @galaxy_sender.dispatch_perform_error_event(@event)
65 | end
66 |
67 | def build_number_string
68 | event = OpenStruct.new(
69 | :agent_status => "online",
70 | :os => "solaris",
71 | :host => "prod1.company.com",
72 | :galaxy_version => "2.6.0.5",
73 | :core_type => "apache",
74 | :machine => "localhost",
75 | :status => "stopped",
76 | :url => "drb://prod2.company.com:4441",
77 | :config_path => "alpha/DEP-1/apache",
78 | :build => "6.1.10",
79 | :ip => "0.1.9.1"
80 | )
81 | assert @galaxy_sender.dispatch_announce_success_event(event)
82 | end
83 | end
84 |
--------------------------------------------------------------------------------
/test/test_fetcher.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/fetcher'
6 | require 'helper'
7 | require 'fileutils'
8 | require 'logger'
9 | require 'webrick'
10 | include WEBrick
11 |
12 | class TestFetcher < Test::Unit::TestCase
13 |
14 | def setup
15 | @local_fetcher = Galaxy::Fetcher.new(File.join(File.dirname(__FILE__), "property_data"), Logger.new("/dev/null"))
16 | @http_fetcher = Galaxy::Fetcher.new("http://localhost:7777", Logger.new("/dev/null"))
17 |
18 | webrick_logger = Logger.new(STDOUT)
19 | webrick_logger.level = Logger::WARN
20 | @server = HTTPServer.new(:Port => 7777, :Logger => webrick_logger)
21 | @server.mount("/", HTTPServlet::FileHandler, File.join(File.dirname(__FILE__), "property_data"), true)
22 | Thread.start do
23 | @server.start
24 | end
25 | end
26 |
27 | def teardown
28 | @server.shutdown
29 | end
30 |
31 | def test_local_fetch
32 | path = @local_fetcher.fetch "foo", "bar", "properties"
33 | assert File.exists?(path)
34 | end
35 |
36 | def test_http_fetch
37 | path = @http_fetcher.fetch "foo", "bar", "properties"
38 | assert File.exists?(path)
39 | end
40 |
41 | def test_http_fetch_not_found
42 | assert_raise RuntimeError do
43 | @server.logger.level = Logger::FATAL
44 | path = @http_fetcher.fetch "gorple", "fez", "properties"
45 | @server.logger.level = Logger::WARN
46 | end
47 | end
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/test/test_filter.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'ostruct'
6 | require 'galaxy/filter'
7 |
8 | class TestFilter < Test::Unit::TestCase
9 | def setup
10 | @null = OpenStruct.new({ })
11 |
12 | @foo = OpenStruct.new({
13 | :host => 'foo',
14 | :ip => '10.0.0.1',
15 | :machine => 'foomanchu',
16 | :config_path => '/alpha/1.0/bloo',
17 | :status => 'running',
18 | })
19 |
20 | @bar = OpenStruct.new({
21 | :host => 'bar',
22 | :ip => '10.0.0.2',
23 | :machine => 'barmanchu',
24 | :config_path => '/beta/2.0/blar',
25 | :status => 'stopped',
26 | })
27 |
28 | @baz = OpenStruct.new({
29 | :host => 'baz',
30 | :ip => '10.0.0.3',
31 | :machine => 'bazmanchu',
32 | :config_path => '/gamma/3.0/blaz',
33 | :status => 'dead',
34 | })
35 |
36 | @blee = OpenStruct.new({
37 | :host => 'blee',
38 | :ip => '10.0.0.4',
39 | :machine => 'bleemanchu',
40 | })
41 |
42 | @agents = [@null, @foo, @bar, @baz, @blee]
43 | end
44 |
45 | def test_filter_none
46 | filter = Galaxy::Filter.new({ })
47 |
48 | assert_equal 0, @agents.select(&filter).size
49 | end
50 |
51 | def test_filter_by_known_host
52 | filter = Galaxy::Filter.new :host => "foo"
53 |
54 | assert_equal [@foo], @agents.select(&filter)
55 | end
56 |
57 | def test_filter_by_unknown_host
58 | filter = Galaxy::Filter.new :host => "unknown"
59 |
60 | assert_equal [ ], @agents.select(&filter)
61 | end
62 |
63 | def test_filter_by_known_machine
64 | filter = Galaxy::Filter.new :machine => "foomanchu"
65 |
66 | assert_equal [@foo], @agents.select(&filter)
67 | end
68 |
69 | def test_filter_by_unknown_machine
70 | filter = Galaxy::Filter.new :machine => "unknown"
71 |
72 | assert_equal [ ], @agents.select(&filter)
73 | end
74 |
75 | def test_filter_by_known_ip
76 | filter = Galaxy::Filter.new :ip => "10.0.0.1"
77 |
78 | assert_equal [@foo], @agents.select(&filter)
79 | end
80 |
81 | def test_filter_by_unknown_ip
82 | filter = Galaxy::Filter.new :ip => "20.0.0.1"
83 |
84 | assert_equal [ ], @agents.select(&filter)
85 | end
86 |
87 | def test_filter_by_state_running
88 | filter = Galaxy::Filter.new :state => "running"
89 |
90 | assert_equal [@foo], @agents.select(&filter)
91 | end
92 |
93 | def test_filter_by_state_stopped
94 | filter = Galaxy::Filter.new :state => "stopped"
95 |
96 | assert_equal [@bar], @agents.select(&filter)
97 | end
98 |
99 | def test_filter_by_state_dead
100 | filter = Galaxy::Filter.new :state => "dead"
101 |
102 | assert_equal [@baz], @agents.select(&filter)
103 | end
104 |
105 | def test_filter_by_unknown_state
106 | filter = Galaxy::Filter.new :state => "unknown"
107 |
108 | assert_equal [ ], @agents.select(&filter)
109 | end
110 |
111 | def test_filter_by_known_env
112 | filter = Galaxy::Filter.new :env => "beta"
113 |
114 | assert_equal [@bar], @agents.select(&filter)
115 | end
116 |
117 | def test_filter_by_known_env
118 | filter = Galaxy::Filter.new :env => "unknown"
119 |
120 | assert_equal [ ], @agents.select(&filter)
121 | end
122 |
123 | def test_filter_by_known_version
124 | filter = Galaxy::Filter.new :version => "1.0"
125 |
126 | assert_equal [@foo], @agents.select(&filter)
127 | end
128 |
129 | def test_filter_by_unknown_version
130 | filter = Galaxy::Filter.new :version => "0.0"
131 |
132 | assert_equal [ ], @agents.select(&filter)
133 | end
134 |
135 | def test_filter_by_known_type
136 | filter = Galaxy::Filter.new :type => "bloo"
137 |
138 | assert_equal [@foo], @agents.select(&filter)
139 | end
140 |
141 | def test_filter_by_unknown_type
142 | filter = Galaxy::Filter.new :type => "unknown"
143 |
144 | assert_equal [ ], @agents.select(&filter)
145 | end
146 |
147 | def test_filter_by_assigned
148 | filter = Galaxy::Filter.new :set => :taken
149 |
150 | assert_equal [@foo, @bar, @baz], @agents.select(&filter)
151 | end
152 |
153 | def test_filter_by_unassigned
154 | filter = Galaxy::Filter.new :set => :empty
155 |
156 | assert_equal [@null, @blee], @agents.select(&filter)
157 | end
158 |
159 | def test_filter_all
160 | filter = Galaxy::Filter.new :set => :all
161 |
162 | assert_equal @agents, @agents.select(&filter)
163 | end
164 |
165 |
166 | #####################################################################################
167 | # The following are additions for GAL-151. Given the way the code is _currently_
168 | # written, we really only need to check against host and machine, but the others are
169 | # added for increased safety and future-proofing
170 | #
171 | def test_filter_by_unknown_host_like_known_host
172 | filter = Galaxy::Filter.new :host => "fo" #don't match with "foo"
173 |
174 | assert_equal [ ], @agents.select(&filter)
175 | end
176 |
177 | def test_filter_by_unknown_machine_like_known_machine
178 | filter = Galaxy::Filter.new :machine => "fooman" # don't match with "foomanchu"
179 |
180 | assert_equal [ ], @agents.select(&filter)
181 | end
182 |
183 | def test_filter_by_unknown_ip_like_known_ip
184 | filter = Galaxy::Filter.new :ip => "10.0.0." # don't match with "10.0.0.1"
185 |
186 | assert_equal [ ], @agents.select(&filter)
187 | end
188 |
189 | def test_filter_by_unknown_env_like_known_env
190 | filter = Galaxy::Filter.new :env => "bet" #don't match with "beta"
191 |
192 | assert_equal [ ], @agents.select(&filter)
193 | end
194 |
195 | def test_filter_by_unknown_version_like_known_version
196 | filter = Galaxy::Filter.new :version => "1." #don't match with "1.0"
197 |
198 | assert_equal [ ], @agents.select(&filter)
199 | end
200 |
201 | def test_filter_by_unknown_type_like_known_type
202 | filter = Galaxy::Filter.new :type => "blo" # don't match with "bloo"
203 |
204 | assert_equal [ ], @agents.select(&filter)
205 | end
206 |
207 | end
208 |
--------------------------------------------------------------------------------
/test/test_host.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 |
3 | require "fileutils"
4 | require "test/unit"
5 | require "galaxy/host"
6 |
7 | class TestHost < Test::Unit::TestCase
8 |
9 | def test_tar_executable_was_found
10 | assert_not_nil Galaxy::HostUtils.tar
11 | end
12 |
13 | def test_system_success
14 | assert_nothing_raised do
15 | Galaxy::HostUtils.system 'true'
16 | end
17 | end
18 |
19 | def test_system_failure
20 | assert_raise Galaxy::HostUtils::CommandFailedError do
21 | Galaxy::HostUtils.system 'false'
22 | end
23 | end
24 |
25 | def test_system_failure_output
26 | begin
27 | Galaxy::HostUtils.system 'ls /gorple/fez'
28 | rescue Exception => e
29 | assert_match(/No such file or directory/, e.message)
30 | end
31 | end
32 | end
--------------------------------------------------------------------------------
/test/test_logger.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/events'
6 | require 'galaxy/host'
7 | require 'galaxy/log'
8 |
9 | class TestLogger < Test::Unit::TestCase
10 | def setup
11 | end
12 |
13 | def teardown
14 | #puts `cat /tmp/galaxy_unit_test.log*`
15 | FileUtils.rm Dir.glob('/tmp/galaxy_unit_test.log*')
16 | end
17 |
18 | def test_initialize_dummy_event_sender
19 | glogger = Galaxy::Log::Glogger.new "/tmp/galaxy_unit_test.log"
20 | assert_kind_of Galaxy::DummyEventSender, glogger.event_dispatcher
21 | assert_kind_of Logger, glogger.log
22 | end
23 |
24 | def test_initialize_collector
25 | glogger = Galaxy::Log::Glogger.new "/tmp/galaxy_unit_test.log", "http://collector.com"
26 | assert_kind_of Galaxy::GalaxyLogEventSender, glogger.event_dispatcher
27 | assert_kind_of Logger, glogger.log
28 | end
29 |
30 | def test_syslog
31 | # Real-life example that was breaking 2.6.pre2
32 | logger = Galaxy::HostUtils.logger
33 | assert logger.debug("http://prod1.company.com:8080/1?v=GalaxyLog,81267034768000%2C4168954152%2C29654%2Csdebug%2Cs%2CsRegistered%2520Event%2520listener%2520type%2520Galaxy%253A%253AGalaxyEventSender%2520at%2520http%253A%252F%252Fz1205a9.company.com%253A8080%252C%2520sender%2520url%25200.1.9.4%2C&rt=b")
34 | assert logger.info("http://prod1.company.com:8080/1?v=GalaxyLog,81267034768000%2C4168954152%2C29654%2Csdebug%2Cs%2CsRegistered%2520Event%2520listener%2520type%2520Galaxy%253A%253AGalaxyEventSender%2520at%2520http%253A%252F%252Fz1205a9.company.com%253A8080%252C%2520sender%2520url%25200.1.9.4%2C&rt=b")
35 | assert logger.warn("http://prod1.company.com:8080/1?v=GalaxyLog,81267034768000%2C4168954152%2C29654%2Csdebug%2Cs%2CsRegistered%2520Event%2520listener%2520type%2520Galaxy%253A%253AGalaxyEventSender%2520at%2520http%253A%252F%252Fz1205a9.company.com%253A8080%252C%2520sender%2520url%25200.1.9.4%2C&rt=b")
36 | assert logger.error("http://prod1.company.com:8080/1?v=GalaxyLog,81267034768000%2C4168954152%2C29654%2Csdebug%2Cs%2CsRegistered%2520Event%2520listener%2520type%2520Galaxy%253A%253AGalaxyEventSender%2520at%2520http%253A%252F%252Fz1205a9.company.com%253A8080%252C%2520sender%2520url%25200.1.9.4%2C&rt=b")
37 | end
38 |
39 | def test_syslog_raw
40 | logger = Galaxy::HostUtils.logger
41 | assert logger << "foo bar baz"
42 | end
43 |
44 | def test_respect_loglevel_with_event_dispatcher
45 | glogger = Galaxy::Log::Glogger.new "/tmp/galaxy_unit_test.log", "non-existent-host", "http://gonsole.test.company.com:1242", "10.15.12.14"
46 | assert_kind_of Galaxy::GalaxyLogEventSender, glogger.event_dispatcher
47 | assert_kind_of Logger, glogger.log
48 |
49 | # Make sure we don't try to send events at the wrong level
50 |
51 | glogger.log.level = Logger::INFO
52 | assert glogger.debug("debug hello from unit test")
53 |
54 | glogger.log.level = Logger::DEBUG
55 | assert_raise URI::BadURIError do
56 | glogger.debug("debug hello from unit test")
57 | end
58 | end
59 |
60 | def test_syslog_level
61 | logger = Galaxy::HostUtils.logger
62 |
63 | assert_equal Logger::INFO, logger.level
64 |
65 | logger.level = Logger::DEBUG
66 | assert_equal Logger::DEBUG, logger.level
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/test/test_logger_collector.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/events'
6 | require 'galaxy/host'
7 | require 'galaxy/log'
8 |
9 | class TestLoggerCollector < Test::Unit::TestCase
10 |
11 | # Set your collector hostname here to run tests.
12 | # See http://github.com/ning/collector
13 | COLLECTOR_HOST = nil
14 |
15 | def test_collectors
16 | unless COLLECTOR_HOST.nil?
17 | send_event_via_event_dispatcher
18 | send_encoded_event_via_event_dispatcher
19 | else
20 | assert true
21 | end
22 | end
23 |
24 | def send_event_via_event_dispatcher
25 | glogger = Galaxy::Log::Glogger.new "/tmp/galaxy_unit_test.log", COLLECTOR_HOST, "http://gonsole.test.company.com:1242", "10.15.12.14"
26 | assert_kind_of Galaxy::GalaxyLogEventSender, glogger.event_dispatcher
27 | assert_kind_of Logger, glogger.log
28 |
29 | assert glogger.event_dispatcher.dispatch_debug_log("debug hello from unit test")
30 | assert glogger.event_dispatcher.dispatch_info_log("info hello from unit test")
31 | assert glogger.event_dispatcher.dispatch_warn_log("warn hello from unit test")
32 | assert glogger.event_dispatcher.dispatch_error_log("error hello from unit test")
33 | assert glogger.event_dispatcher.dispatch_fatal_log("fatal hello from unit test")
34 | end
35 |
36 | def send_encoded_event_via_event_dispatcher
37 | glogger = Galaxy::Log::Glogger.new "/tmp/galaxy_unit_test.log", COLLECTOR_HOST, "http://gonsole.test.company.com:1242", "10.15.12.14"
38 | assert_kind_of Galaxy::GalaxyLogEventSender, glogger.event_dispatcher
39 | assert_kind_of Logger, glogger.log
40 |
41 | assert glogger.event_dispatcher.dispatch_error_log("i love spaces")
42 | assert glogger.event_dispatcher.dispatch_error_log("drb://slashespowaa.com")
43 | assert glogger.event_dispatcher.dispatch_error_log("Embedded Thrift: ,sMyThrift,412")
44 | assert glogger.event_dispatcher.dispatch_error_log("$rr0r haZ !@#\$%^&*()_+{}:<>?/.,';#][\/~-`")
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/test/test_parallelize.rb:
--------------------------------------------------------------------------------
1 | require 'test/unit'
2 | require 'galaxy/parallelize'
3 |
4 | class TestParallelize < Test::Unit::TestCase
5 | def test_parallelize_with_thread_count_of_1
6 | array = (1..10).entries
7 | start = Time.new
8 | array.parallelize(1) { |i| sleep 1 }
9 | stop = Time.new
10 | assert stop - start >= 10
11 | assert stop - start < 11
12 | end
13 |
14 | def test_parallelize_with_thread_count_of_10
15 | array = (1..100).entries
16 | start = Time.new
17 | array.parallelize(10) { |i| sleep 1 }
18 | stop = Time.new
19 | assert stop - start >= 10
20 | assert stop - start < 11
21 | end
22 |
23 | def test_parallelize_with_thread_count_of_100
24 | array = (1..1000).entries
25 | start = Time.new
26 | array.parallelize(100) { |i| sleep 1 }
27 | stop = Time.new
28 | assert stop - start >= 10
29 | assert stop - start < 11
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/test/test_propbuilder.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 |
3 | require 'test/unit'
4 | require "galaxy/properties"
5 | require 'logger'
6 |
7 | class TestPropertyBuilder < Test::Unit::TestCase
8 |
9 | PropertyBase = File.dirname(__FILE__) + "/property_data"
10 |
11 | def setup
12 | @builder = Galaxy::Properties::Builder.new PropertyBase, Logger.new("/dev/null")
13 | end
14 |
15 | def test_simple
16 | props = @builder.build "/a/b/c/d", "test_simple.properties"
17 | assert_equal "green", props['chris']
18 | end
19 |
20 | def test_override
21 | props = @builder.build "/a/b/c/d", "test_override.properties"
22 | assert_equal "purple", props['oscar']
23 | assert_equal "red", props['sam']
24 | end
25 |
26 | def test_comments_ignored
27 | props = @builder.build "/a/b/c/d", "test_comments_ignored.properties"
28 |
29 | assert_nil props['hello']
30 | assert_nil props['#hello']
31 | assert_equal "fuschia", props['red']
32 | end
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/test/test_repository.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 |
3 | require 'test/unit'
4 | require "galaxy/repository"
5 |
6 | class TestRepository < Test::Unit::TestCase
7 |
8 | PropertyBase = File.dirname(__FILE__) + "/property_data"
9 |
10 | def setup
11 | @builder = Galaxy::Repository.new PropertyBase
12 | end
13 |
14 | def test_simple
15 | @builder.walk "/a/b/c/d", "test_simple.properties" do |path, content|
16 | assert_equal "/a/b/c/test_simple.properties", path
17 | end
18 | end
19 |
20 | def test_multiple
21 | paths = []
22 | @builder.walk "/a/b/c/d", "test_override.properties" do |path, content|
23 | paths << path
24 | end
25 |
26 | assert_equal ["/a/b/test_override.properties", "/a/b/c/d/test_override.properties"], paths
27 | end
28 |
29 | def test_empty
30 | paths = []
31 | @builder.walk "/a/b/c/d", "empty.properties" do |path, content|
32 | paths << path
33 | end
34 |
35 | assert_equal [], paths
36 | end
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/test/test_temp.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 |
3 | require "fileutils"
4 | require "test/unit"
5 | require "galaxy/temp"
6 |
7 | class TestTemp < Test::Unit::TestCase
8 |
9 | def test_simple
10 | begin
11 | file = Galaxy::Temp.mk_file
12 | dir = Galaxy::Temp.mk_dir
13 | assert File.exists?(file)
14 | assert File.exists?(dir)
15 | ObjectSpace.garbage_collect
16 | assert File.exists?(file)
17 | assert File.exists?(dir)
18 | ensure
19 | FileUtils.rm file if File.exists? file
20 | FileUtils.rmdir dir if File.exists? dir
21 | end
22 | end
23 |
24 | def test_repeated
25 | used_files = []
26 | used_dirs = []
27 | begin
28 | 100.times do
29 | file = Galaxy::Temp.mk_file
30 | assert !used_files.include?(file)
31 | assert !used_dirs.include?(file)
32 | used_files.push file
33 | dir = Galaxy::Temp.mk_dir
34 | assert !used_files.include?(dir)
35 | assert !used_dirs.include?(dir)
36 | used_dirs.push dir
37 | assert File.exists?(file)
38 | assert File.exists?(dir)
39 | ObjectSpace.garbage_collect
40 | assert File.exists?(file)
41 | assert File.exists?(dir)
42 | end
43 | ensure
44 | used_files.each { |file| FileUtils.rm file if File.exists? file }
45 | end
46 | end
47 |
48 | def test_auto
49 | rd, wr = IO.pipe
50 | if fork
51 | wr.close
52 | file, dir = rd.read.split "\t"
53 | rd.close
54 | Process.wait
55 | begin
56 | assert !File.exists?(file)
57 | assert !File.exists?(dir)
58 | ensure
59 | FileUtils.rm file if File.exists? file
60 | FileUtils.rmdir dir if File.exists? dir
61 | end
62 | else
63 | rd.close
64 | file = Galaxy::Temp.mk_auto_file
65 | dir = Galaxy::Temp.mk_auto_dir
66 | assert File.exists?(file)
67 | assert File.exists?(dir)
68 | ObjectSpace.garbage_collect
69 | assert File.exists?(file)
70 | assert File.exists?(dir)
71 | wr.write "#{file}\t#{dir}"
72 | wr.close
73 | exit 0
74 | end
75 | end
76 |
77 | end
78 |
--------------------------------------------------------------------------------
/test/test_transport.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2 | $:.unshift File.join(File.dirname(__FILE__))
3 |
4 | require 'test/unit'
5 | require 'galaxy/transport'
6 | require 'galaxy/console'
7 |
8 | class TestTransport < Test::Unit::TestCase
9 | def test_handler_for
10 | assert Galaxy::Transport.handler_for("druby://xxxx:444").kind_of?(Galaxy::DRbTransport)
11 | assert Galaxy::Transport.handler_for("local://xxxx:444").kind_of?(Galaxy::LocalTransport)
12 | assert Galaxy::Transport.handler_for("http://xxxx:444").kind_of?(Galaxy::HttpTransport)
13 | end
14 |
15 | def test_handler_not_found
16 | assert_raises RuntimeError do
17 | Galaxy::Transport.handler_for("invalid://xxxx:444")
18 | end
19 | end
20 |
21 | def test_drb_publish
22 | url = "druby://localhost:4444"
23 | console = Galaxy::Transport.publish url, "hello"
24 |
25 | obj = Galaxy::Transport.locate url
26 |
27 | assert_equal "hello", obj.to_s
28 | console.stop_service
29 | end
30 |
31 | def test_drb_pool_size
32 | assert_equal 0, DRb::DRbConn::POOL_SIZE
33 | end
34 |
35 | def test_http_publish
36 | console = Galaxy::Console.start({ :host => 'localhost', :log_level => Logger::WARN })
37 | url = "http://localhost:4441"
38 |
39 | assert_raises TypeError do
40 | Galaxy::Transport.publish url, nil
41 | end
42 |
43 | console_logger = Logger.new(STDOUT)
44 | console_logger.level = Logger::WARN
45 | Galaxy::Transport.publish url, console, console_logger
46 |
47 | announcer = Galaxy::Transport.locate url
48 | o = OpenStruct.new(:host => "localhost", :url => url, :status => "running")
49 | assert_equal Galaxy::ReceiveAnnouncement::ANNOUNCEMENT_RESPONSE_TEXT, announcer.announce(o)
50 |
51 | Galaxy::Transport.unpublish url
52 | console.shutdown
53 | end
54 |
55 | def foo(a)
56 | $foo_called = true
57 | end
58 |
59 | # def test_http_publish_with_callback
60 | # url = "http://localhost:4442"
61 | # Galaxy::Transport.publish url, lambda{|a| foo(a) }
62 | #
63 | # ann = Galaxy::Transport.locate url
64 | # $foo_called = false
65 | # ann.announce("announcement")
66 | # assert $foo_called == true
67 | #
68 | # Galaxy::Transport.unpublish url
69 | # end
70 |
71 | end
72 |
--------------------------------------------------------------------------------