fum 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,44 @@
1
+ module Fum
2
+ module CommandManager
3
+
4
+ module ClassMethods
5
+
6
+ @@commands_map = Hash.new
7
+
8
+ def command(name, opts={})
9
+ require "fum/commands/#{name}"
10
+ class_name = name.to_s.capitalize
11
+ @@commands_map[name] = Fum::Commands::const_get(class_name)
12
+ end
13
+
14
+ def create_commands(*args)
15
+ map = {}
16
+ @@commands_map.each { |key, value|
17
+ map[key] = value.new(*args)
18
+ }
19
+ map
20
+ end
21
+ end
22
+
23
+ def self.included(includer)
24
+ includer.extend ClassMethods
25
+ end
26
+
27
+ def initialize
28
+ @commands = self.class.create_commands(self)
29
+ end
30
+
31
+ def commands
32
+ @commands
33
+ end
34
+
35
+ def parse_command_options(name)
36
+ @commands[name].parse_options
37
+ end
38
+
39
+ def run_command(name, options = {})
40
+ @commands[name].execute(options)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,58 @@
1
+ module Fum
2
+ module Commands
3
+ class Events < Fum::Command
4
+
5
+ def parse_options
6
+ Trollop::options do
7
+ banner "usage: events [options], where options are:"
8
+ opt :number, "Number of events to show", :type => :integer, :default => 100
9
+ opt :application, "Show events only for the specified application", :type => :string
10
+ opt :environment, "Show events associated with the specified environment name or id", :type => :string
11
+ opt :severity, "Limit events to severity or higher", :type => :string
12
+ opt :version_label, "Version label", :type => :string, :long => "version"
13
+ end
14
+ end
15
+
16
+ def execute(options)
17
+ beanstalk = Fog::AWS[:beanstalk]
18
+
19
+ filters = {
20
+ 'MaxRecords' => options[:number],
21
+ 'ApplicationName' => options[:application],
22
+ 'VersionLabel' => options[:version_label]
23
+ }
24
+ filters['Severity'] = options[:severity].upcase unless options[:severity].nil?
25
+
26
+ if options[:environment] && options[:environment].match(/e-[a-zA-Z0-9]{10}/)
27
+ filters['EnvironmentId'] = options[:environment]
28
+ else
29
+ filters['EnvironmentName'] = options[:environment]
30
+ end
31
+
32
+ filters = filters.delete_if { |key, value| value.nil? }
33
+
34
+ events = beanstalk.events.all(filters).reverse
35
+
36
+ # Beanstalk seems to return an extra item
37
+ events.shift
38
+
39
+ events.each { |event|
40
+ app = event.application_name
41
+ if event.environment_name
42
+ app += "(#{event.environment_name})"
43
+ elsif event.version_label
44
+ app += "(#{event.version_label})"
45
+ elsif event.template_name
46
+ app += "(#{event.template_name})"
47
+ end
48
+
49
+ puts "#{event.date.strftime('%b %d %H:%M:%S')} #{app} #{event.severity} \"#{event.message}\""
50
+ }
51
+
52
+ end
53
+
54
+ end
55
+
56
+
57
+ end
58
+ end
@@ -0,0 +1,110 @@
1
+ module Fum
2
+ module Commands
3
+ class Launch < Fum::Command
4
+ include Fum::DNS
5
+
6
+ def parse_options
7
+ options = Trollop::options do
8
+ banner "usage launch [options] <environment-id>, where options are:"
9
+ opt :version_label, "Launch the specified version", :long => "version", :type => :string
10
+ opt :create, "Create application if it does not exist"
11
+ opt :no_dns, "Do not change any DNS records", :default => false
12
+ opt :no_wait, "Launch environment, but do not wait for ready", :default => false, :short => 'w'
13
+ end
14
+ if ARGV.empty?
15
+ die "Stage not specified. Please specify a stage to launch"
16
+ end
17
+ options[:stage_name] = ARGV.shift
18
+ options
19
+ end
20
+
21
+ def execute(options)
22
+ app = @application.main_decl
23
+
24
+ stage_name = options[:stage_name]
25
+ stage_decl = stage(@application.main_decl, stage_name)
26
+
27
+ if options[:verbose]
28
+ puts "Verifying application '#{app.name}' exists in AWS."
29
+ end
30
+
31
+ beanstalk = Fog::AWS[:beanstalk]
32
+
33
+ beanstalk_app = beanstalk.applications.get(app.name)
34
+
35
+ if beanstalk_app.nil?
36
+ if options[:create]
37
+ beanstalk_app = beanstalk.applications.create(:name => app.name)
38
+ beanstalk.versions.create({
39
+ :application_name => app.name,
40
+ :label => 'Sample'
41
+ })
42
+ else
43
+ die "Could not find app '#{app.name}' in AWS account."
44
+ end
45
+
46
+ end
47
+
48
+ env_opt = {
49
+ :application_name => app.name,
50
+ :name => stage_decl.environment_name
51
+ }
52
+ env_opt[:template_name] = stage_decl.template_name unless stage_decl.template_name.nil?
53
+ env_opt[:solution_stack_name] = stage_decl.solution_stack_name unless stage_decl.solution_stack_name.nil?
54
+ env_opt[:description] = stage_decl.env_description unless stage_decl.env_description.nil?
55
+
56
+ set_version(stage_decl, env_opt, options)
57
+
58
+ if options[:verbose] || options[:noop]
59
+ puts "Launching an environment named '#{stage_decl.environment_name}'"
60
+ end
61
+
62
+ begin
63
+ new_env = beanstalk.environments.create(env_opt) unless options[:noop]
64
+ rescue Exception => e
65
+ die "Problem creating environment: #{e.to_s}"
66
+ end
67
+
68
+ puts "Launched environment '#{stage_decl.environment_name}'."
69
+
70
+ puts "Waiting for environment to become ready..."
71
+
72
+ new_env.wait_for { ready? } unless options[:noop]
73
+
74
+ puts "New environment is ready."
75
+
76
+ puts "Waiting for environment to become healthy..."
77
+
78
+ new_env.wait_for { healthy? } unless options[:noop]
79
+
80
+ puts "New environment is healthy."
81
+
82
+ update_zones(stage_decl, new_env, options) unless options[:no_dns]
83
+
84
+ end
85
+
86
+ def set_version(stage_decl, env_opt, options)
87
+ version_label = stage_decl.version_label
88
+ return nil if version_label.nil?
89
+
90
+ if version_label.is_a?(String)
91
+ env_opt[:version_label] = version_label
92
+ puts "Using verison label #{version_label}"
93
+ elsif version_label.is_a?(Hash) && version_label.has_key?(:from_stage)
94
+ from_stage = version_label[:from_stage]
95
+ from_stage_decl = stage(@application.main_decl, from_stage)
96
+ analyzer = StageAnalyzer.new(from_stage_decl)
97
+ analyzer.analyze(options)
98
+ active = analyzer.active
99
+ die "Cannot determine version to launch. No active environment for stage '#{from_stage}' specified." if active.nil?
100
+ env_opt[:version_label] = active.version_label
101
+ puts "Using version #{active.version_label} from stage #{from_stage}."
102
+ else
103
+ "Unknown version label #{version_label.inspect}"
104
+ end
105
+
106
+
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,83 @@
1
+ module Fum
2
+ module Commands
3
+ class List < Fum::Command
4
+ include Fum::DNS
5
+
6
+ def parse_options
7
+ opts = Trollop::options do
8
+ banner "usage: list [options] <environment-id>, where options are:"
9
+ end
10
+ if ARGV.empty?
11
+ die "Please specify a beanstalk type to list!"
12
+ end
13
+
14
+ object = ARGV.shift
15
+
16
+ opts[:type] = case object
17
+ when "stacks"
18
+ :stacks
19
+ when "app", "apps", "applications"
20
+ :applications
21
+ when "env", "envs", "environments"
22
+ :environments
23
+ when "stages"
24
+ :stages
25
+ when "templates"
26
+ :templates
27
+ when "versions"
28
+ :versions
29
+ else
30
+ die "Unknown object to list #{object}."
31
+ end
32
+
33
+ opts
34
+ end
35
+
36
+ #
37
+ #
38
+ # * :type
39
+ def execute(options)
40
+ beanstalk = Fog::AWS[:beanstalk]
41
+
42
+ case options[:type]
43
+ when :stacks
44
+ beanstalk.solution_stacks.each { |stack|
45
+ puts stack["SolutionStackName"]
46
+ }
47
+ when :applications
48
+ beanstalk.applications.each { |app|
49
+ puts app.name
50
+ }
51
+ when :environments
52
+ list_environments(options)
53
+ when :stages
54
+ @application.main_decl.stages.each_value { |stage|
55
+ puts stage.id
56
+ }
57
+ when :templates
58
+ beanstalk.templates.each { |template|
59
+ puts template.name
60
+ }
61
+ when :versions
62
+ beanstalk.versions.each { |version|
63
+ puts version.label
64
+ }
65
+ else
66
+ die "Unknown object to list #{object}."
67
+ end
68
+
69
+ end
70
+
71
+ def list_environments(options)
72
+ beanstalk = Fog::AWS[:beanstalk]
73
+ environments = beanstalk.environments
74
+
75
+ environments.each { |env|
76
+ puts env.name
77
+ }
78
+
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,45 @@
1
+ module Fum
2
+ module Commands
3
+ class Repair < Fum::Command
4
+ include Fum::DNS
5
+
6
+ def parse_options
7
+ opts = Trollop::options do
8
+ banner "usage: repair [options] <stage-id>, where options are:"
9
+ opt :environment, "Environment name to repair", :type => :string
10
+ end
11
+ if ARGV.empty?
12
+ die "Please specify a stage name type to repair."
13
+ end
14
+ opts[:stage_name] = ARGV.shift
15
+
16
+ opts
17
+ end
18
+
19
+ #
20
+ #
21
+ # * :type
22
+ def execute(options)
23
+ stage_name = options[:stage_name]
24
+ stage_decl = stage(@application.main_decl, stage_name)
25
+
26
+ analyzer = StageAnalyzer.new(stage_decl)
27
+
28
+ analyzer.analyze(options)
29
+
30
+ degraded = analyzer.env_map.each_value.select { |env| env[:state] == :degraded }
31
+
32
+ if degraded.length == 1
33
+ degraded = degraded.shift
34
+ puts "Repairing environment #{degraded[:env].name}."
35
+ update_zones(stage_decl, degraded[:env], options)
36
+ puts "Repair complete."
37
+ else
38
+ puts "Nothing to repair."
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ module Fum
2
+ module Commands
3
+ class Status < Fum::Command
4
+ include Fum::DNS
5
+
6
+ def parse_options
7
+ opts = Trollop::options do
8
+ banner "usage: status [options] <stage-name>, where options are:"
9
+ end
10
+ if ARGV.empty?
11
+ die "Please specify a stage name."
12
+ end
13
+
14
+ opts[:stage_name] = ARGV.shift
15
+
16
+ opts
17
+ end
18
+
19
+ #
20
+ #
21
+ # * :type
22
+ def execute(options)
23
+
24
+ stage_name = options[:stage_name]
25
+ stage_decl = stage(@application.main_decl, stage_name)
26
+
27
+ analyzer = StageAnalyzer.new(stage_decl)
28
+ analyzer.analyze(options)
29
+
30
+ envs = analyzer.env_map.values
31
+
32
+ unless envs.empty?
33
+ envs.each { |env|
34
+ puts "#{env[:env].name} (#{env[:state]})"
35
+ }
36
+ else
37
+ puts "No environments found for stage #{stage_name}."
38
+ end
39
+
40
+
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,89 @@
1
+ module Fum
2
+ module Commands
3
+ class Tail < Fum::Command
4
+
5
+ def parse_options
6
+ Trollop::options do
7
+ banner "usage: tail [options], where options are:"
8
+ opt :application, "Show events only for the specified application", :type => :string
9
+ opt :environment, "Show events associated with the specified environment name or id", :type => :string
10
+ opt :directory, "Output log files to the directory specified.", :type => :string
11
+ end
12
+ end
13
+
14
+ def execute(options)
15
+ beanstalk = Fog::AWS[:beanstalk]
16
+
17
+ die "Directory #{options[:directory]} does not exist" if options[:directory] && !File.directory?(options[:directory])
18
+
19
+ if options[:environment].nil?
20
+ # Use the currently active environment
21
+
22
+ end
23
+ request_opts = {
24
+ 'ApplicationName' => options[:application],
25
+ 'EnvironmentName' => options[:environment],
26
+ 'InfoType' => 'tail'
27
+ }
28
+
29
+ if options[:environment] && options[:environment].match(/e-[a-zA-Z0-9]{10}/)
30
+ request_opts['EnvironmentId'] = options[:environment]
31
+ else
32
+ request_opts['EnvironmentName'] = options[:environment]
33
+ end
34
+
35
+ puts "Requesting logs for environment #{options[:environment]}" if options[:verbose]
36
+ beanstalk.request_environment_info(request_opts)
37
+
38
+ begin
39
+ files = Timeout::timeout(360) do
40
+ # Sleep a few seconds
41
+ sleep(5)
42
+ begin
43
+ puts "Retrieving logs for environment #{options[:environment]}" if options[:verbose]
44
+ info = beanstalk.retrieve_environment_info(request_opts).body["RetrieveEnvironmentInfoResult"]["EnvironmentInfo"]
45
+ puts "Logs not yet available, will try again in 10 seconds." if options[:verbose] && info.length == 0
46
+ end while info.length == 0 && sleep(10)
47
+
48
+ info = info.sort_by { |i| i['SampleTimestamp'] }.reverse
49
+ instance_ids = []
50
+ info = info.select { |i|
51
+ seen = instance_ids.include?(i['Ec2InstanceId'])
52
+ instance_ids << i['Ec2InstanceId']
53
+ !seen
54
+ }
55
+ end
56
+ rescue Timeout::Error
57
+ die "Operation timed out, could not retrieve environment info."
58
+ end
59
+
60
+
61
+ require 'excon'
62
+ files.each { |file|
63
+ contents = Excon.get(file['Message']).body
64
+ if options[:directory]
65
+ filename = "#{file['Ec2InstanceId']}-#{file['SampleTimestamp'].strftime('%Y%m%dT%H%M')}.txt"
66
+ file = File.join(File.expand_path(options[:directory]),filename)
67
+ if File.exists?(file)
68
+ puts "File already exists #{file}, will not overwrite."
69
+ next
70
+ end
71
+ File.open(file, 'w') { |f|f.write(contents) }
72
+ puts "Created file #{file}"
73
+ else
74
+ puts "##########################################"
75
+ puts "# InstanceID #{file['Ec2InstanceId']}"
76
+ puts "# Timestamp #{file['SampleTimestamp']}"
77
+ puts "##########################################"
78
+ puts contents
79
+ end
80
+
81
+ }
82
+
83
+ end
84
+
85
+ end
86
+
87
+
88
+ end
89
+ end