lucid-cumulus 0.11.2 → 0.11.3

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,20 @@
1
+ module Cumulus
2
+ module AutoScaling
3
+ require "common/Commands"
4
+ class Commands < Cumulus::Common::Commands
5
+
6
+ def self.banner_message
7
+ format_message [
8
+ "autoscaling: Manage AutoScaling groups.",
9
+ "\tCompiles AutoScaling groups, scaling policies, and alarms that are defined in configuration files and syncs the resulting AutoScaling groups with AWS.",
10
+ ]
11
+ end
12
+
13
+ def self.manager
14
+ require "autoscaling/manager/Manager"
15
+ Cumulus::AutoScaling::Manager.new
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -1,17 +1,35 @@
1
1
  require "aws-sdk"
2
+ require "json"
3
+ require "deepsort"
2
4
 
3
5
  module AwsExtensions
4
6
  module S3
5
7
  module BucketPolicy
6
8
  # Public: Method that will either return the bucket policy, or an empty
7
- # string if there is no policy. We have to do this because catching an
8
- # exception is the ONLY way to determine if there is a policy.
9
+ # string if there is no policy.
9
10
  #
10
11
  # Returns the policy as a string.
11
12
  def policy_string
12
- policy.string
13
+ # fetch the sorted policy
14
+ hash = policy_hash
15
+ # check if policy exists
16
+ unless hash.nil?
17
+ # convert the policy to string
18
+ JSON.generate(hash)
19
+ else
20
+ # if no policy exists, return an empty string
21
+ ""
22
+ end
23
+ end
24
+
25
+ # Public: Method returns the bucket policy as a sorted hash
26
+ # if no policy exists, returns nil
27
+ def policy_hash
28
+ # rescue and ignore all excpetions related to no policy existing
29
+ # We have to do this because catching an exception is the ONLY way to determine if there is a policy.
30
+ JSON.parse(policy.string).deep_sort
13
31
  rescue Aws::S3::Errors::NoSuchBucketPolicy
14
- ""
32
+ nil
15
33
  end
16
34
  end
17
35
  end
@@ -0,0 +1,51 @@
1
+ module Cumulus
2
+ module CloudFront
3
+ require "common/Commands"
4
+ class Commands < Cumulus::Common::Commands
5
+
6
+ def self.banner_message
7
+ format_message [
8
+ "cloudfront: Manage CloudFront",
9
+ "\tDiff and sync CloudFront configuration with AWS.",
10
+ ]
11
+ end
12
+
13
+ def self.command_details
14
+ format_message [
15
+ ["diff", "print out differences between local configuration and AWS (supplying the id of the distribution will diff only that distribution)"],
16
+ ["invalidate", "create an invalidation. Must supply the name of the invalidation to run. Specifying 'list' as an argument lists the local invalidation configurations"],
17
+ ["list", "list the locally defined distributions"],
18
+ ["migrate", "produce Cumulus CloudFront distribution configuration from current AWS configuration"],
19
+ ["sync", "sync local cloudfront distribution configuration with AWS (supplying the id of the distribution will sync only that distribution)"],
20
+ ]
21
+ end
22
+
23
+ def self.manager
24
+ require "cloudfront/manager/Manager"
25
+ Cumulus::CloudFront::Manager.new
26
+ end
27
+
28
+ def self.valid_options
29
+ [["diff", "invalidate", "list", "migrate", "sync"]]
30
+ end
31
+
32
+ def self.execute(arguments)
33
+ if arguments[0] == "invalidate"
34
+ if arguments.size != 2
35
+ puts "Specify one invalidation to run"
36
+ exit
37
+ else
38
+ if arguments[1] == "list"
39
+ manager.list_invalidations
40
+ else
41
+ manager.invalidate(arguments[1])
42
+ end
43
+ end
44
+ else
45
+ super(arguments)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -27,7 +27,7 @@ module Cumulus
27
27
  # json - indicates if the resources are in json format
28
28
  # loader - the function that will handle the read json
29
29
  def self.resource(file, dir, json = true, &loader)
30
- name = file.end_with?(".json") ? file.chomp(".json") : file
30
+ name = file.chomp(".json")
31
31
 
32
32
  begin
33
33
  contents = load_file(file, dir)
@@ -46,12 +46,14 @@ module Cumulus
46
46
  #
47
47
  # file - the name of the file to load
48
48
  # dir - the directory the file is located in
49
- # vars - the variables to apply to the template
49
+ # vars - the variables to apply to the template (can be nil)
50
50
  # loader - the function that will handle the read json
51
51
  def self.template(file, dir, vars, &loader)
52
52
  template = load_file(file, dir)
53
- vars.each do |key, value|
54
- template.gsub!("{{#{key}}}", "#{value}")
53
+ if vars
54
+ vars.each do |key, value|
55
+ template.gsub!("{{#{key}}}", "#{value}")
56
+ end
55
57
  end
56
58
  json = JSON.parse(template)
57
59
 
@@ -0,0 +1,131 @@
1
+ module Cumulus
2
+ module Common
3
+ # Public: Base class for the command line parser class.
4
+ #
5
+ # Classes that extend this class must provide the following methods:
6
+ #
7
+ # self.manager - returns the manager for the AWS module that is being used.
8
+ #
9
+ # Additionally, the following methods can be set to change the behavior of the parser:
10
+ #
11
+ # self.usage_message - returns the usage instructions.
12
+ # self.banner_message - returns the title, purpose, and behavior of the module. This is displayed in the help message.
13
+ # self.command_details - returns basic instructions on how to use each module command. This is displayed in the help message.
14
+ # self.valid_options - returns an array of the valid arguments where each argument is an array of valid commands.
15
+ # self.verify - returns true/false of whether or not the arguments passed in are valid (proper order, right commands, etc).
16
+ # self.execute - runs the correct method on the manager based on the array of arguments passed in.
17
+ #
18
+ # For your convenience, many of the help and usage messages can be formatted correctly with the self.format_message method.
19
+ # To use this method, pass in the message as an array of 'lines' where each line is either a string, or a command-instruction pair.
20
+ #
21
+ # The following super class is one example of what each method could look like. Change them in inherited classes as necessary.
22
+ class Commands
23
+ def self.usage_message
24
+ options = valid_options
25
+ options[0] = options[0].push("help")
26
+ "Usage: cumulus #{manager_name}" + options.reduce(String.new) do |memo, param_list|
27
+ memo + " [" + param_list.join("|") + "]"
28
+ end + " <asset>"
29
+ end
30
+
31
+ def self.banner_message
32
+ format_message [
33
+ "#{manager_name}: Manage #{manager_name.upcase}s.",
34
+ "\tCompiles #{manager_name.upcase}s that are defined with configuration files and syncs the resulting #{manager_name.upcase} assets with AWS.",
35
+ ]
36
+ end
37
+
38
+ def self.command_details
39
+ format_message [
40
+ ["diff", "get a list of resources that have different definitions locally than in AWS (supplying the name of the group will diff only that group)"],
41
+ ["list", "list the resources defined in configuration"],
42
+ ["migrate", "create resource configuration files that match the definitions in AWS"],
43
+ ["sync", "sync the local resource definition with AWS (supplying the name of the resource will sync only that group). Also adds and removes users from groups"],
44
+ ]
45
+ end
46
+
47
+ def self.help_message
48
+ format_message [
49
+ "#{banner_message}",
50
+ "",
51
+ "#{usage_message}",
52
+ "",
53
+ "Commands",
54
+ "#{command_details}"
55
+ ]
56
+ end
57
+
58
+ def self.manager
59
+ require "common/manager/Manager"
60
+ Cumulus::Common::Manager.new
61
+ end
62
+
63
+ # Retrieves the AWS module name by checking what ruby module the class is in.
64
+ def self.manager_name
65
+ manager.class.to_s.split("::")[1].downcase
66
+ end
67
+
68
+ def self.valid_options
69
+ # The first array is the list of all possible first commands
70
+ # The second array is the list of all possible second commands and so on.
71
+ [["diff", "list", "migrate", "sync"]]
72
+ end
73
+
74
+ def self.verify(arguments)
75
+ (arguments.size >= valid_options.size) and
76
+ (valid_options.size < 1 or valid_options[0].include?(arguments[0])) and
77
+ (valid_options.size < 2 or valid_options[1].include?(arguments[1])) and
78
+ (valid_options.size < 3 or valid_options[2].include?(arguments[2]))
79
+ end
80
+
81
+ def self.execute(arguments)
82
+ if arguments[0] == "diff" and arguments.size == 2
83
+ manager.diff_one(arguments[1])
84
+ elsif arguments[0] == "sync" and arguments.size == 2
85
+ manager.sync_one(arguments[1])
86
+ else
87
+ manager.method(arguments[0]).call
88
+ end
89
+ end
90
+
91
+ # use this helper function to format help messages
92
+ def self.format_message(message, args = Hash.new)
93
+ # default pad is the size of the smallest command
94
+ pad = args.key?(:padding) ? args[:padding] : message.reduce(0) do |memo, line|
95
+ if line.class == Array && line.first.size > memo
96
+ line.first.size
97
+ else
98
+ memo
99
+ end
100
+ end
101
+
102
+ message = message.reduce(String.new) do |memo, line|
103
+ if line.class == Array
104
+ memo + "\t%-#{pad}s - %s\n" % line
105
+ else
106
+ memo + line + "\n"
107
+ end
108
+ end.chomp
109
+
110
+ message = "\t"*args[:indent] + message.gsub("\n", "\n" + "\t"*args[:indent]) if args.key?(:indent)
111
+
112
+ message
113
+ end
114
+
115
+ # the main function called by the command line parser. DON'T OVERRIDE
116
+ def self.parse(arguments)
117
+ if arguments.size >= 1 and arguments[0] == "help"
118
+ puts help_message
119
+ exit
120
+ end
121
+
122
+ if !verify(arguments)
123
+ puts usage_message
124
+ exit
125
+ end
126
+
127
+ execute(arguments)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -69,10 +69,10 @@ module Cumulus
69
69
  value
70
70
  end
71
71
  rescue KeyError => e
72
- puts "Your configuration file is missing $.#{key}."
73
72
  if allow_missing
74
73
  nil
75
74
  else
75
+ puts "Your configuration file is missing $.#{key}."
76
76
  exit
77
77
  end
78
78
  end
@@ -91,15 +91,16 @@ module Cumulus
91
91
  # Internal: Constructor. Sets up the `instance` variable, which is the access
92
92
  # point for the Singleton.
93
93
  #
94
+ # json - a Hash containing the json configuration. If nil, this value will be read in from the default location
94
95
  # conf_dir - The String path to the directory the configuration can be found in
95
96
  # profile - The String profile name that will be used to make AWS API calls
96
97
  # assume_role - The ARN of the role to assume when making AWS API calls
97
98
  # autoscaling_force_size
98
99
  # - Determines whether autoscaling should use configured values for
99
100
  # min/max/desired group size
100
- def initialize(conf_dir, profile, assume_role, autoscaling_force_size)
101
- Config.conf_dir = conf_dir;
102
- Config.json = JSON.parse(File.read(absolute_path("configuration.json")))
101
+ def initialize(conf_dir, profile, assume_role, autoscaling_force_size, json = nil)
102
+ Config.conf_dir = conf_dir
103
+ Config.json = json.nil? ? JSON.parse(File.read(absolute_path("configuration.json"))) : json
103
104
  @colors_enabled = conf "colors-enabled"
104
105
  @iam = IamConfig.new
105
106
  @autoscaling = AutoScalingConfig.new(autoscaling_force_size)
@@ -126,6 +127,7 @@ module Cumulus
126
127
  :region => region,
127
128
  :profile => profile,
128
129
  :credentials => credentials,
130
+ :stub_responses => conf("stub_aws_responses", true)
129
131
  }.reject { |_, v| v.nil? }
130
132
  end
131
133
 
@@ -140,8 +142,22 @@ module Cumulus
140
142
  # - Determines whether autoscaling should use configured values for
141
143
  # min/max/desired group size
142
144
  def init(conf_dir, profile, assume_role, autoscaling_force_size)
143
- instance = new(conf_dir, profile, assume_role, autoscaling_force_size)
144
- @@instance = instance
145
+ @@instance = new(conf_dir, profile, assume_role, autoscaling_force_size)
146
+ end
147
+
148
+ # Private: Initialize the Configuration Singleton. Must be called before any
149
+ # access to `Configuration.instance` is used. Used by the tests to pass in json
150
+ # rather than a file
151
+ #
152
+ # json - the Hash containing the json configuration
153
+ # conf_dir - The String path to the directory the configuration can be found in
154
+ # profile - The String profile name that will be used to make AWS API calls
155
+ # assume_role - The ARN of the role to assume when making AWS API calls
156
+ # autoscaling_force_size
157
+ # - Determines whether autoscaling should use configured values for
158
+ # min/max/desired group size
159
+ def init_from_hash(json, conf_dir, profile, assume_role, autoscaling_force_size)
160
+ @@instance = new(conf_dir, profile, assume_role, autoscaling_force_size, json)
145
161
  end
146
162
 
147
163
  # Public: The Singleton instance of Configuration.
@@ -152,6 +168,7 @@ module Cumulus
152
168
  end
153
169
 
154
170
  private :new
171
+ private :init_from_hash
155
172
  end
156
173
 
157
174
  # Public: Inner class that contains IAM configuration options
@@ -0,0 +1,51 @@
1
+ module Cumulus
2
+ module EC2
3
+ require "common/Commands"
4
+ class Commands < Cumulus::Common::Commands
5
+
6
+ def self.command_details
7
+ format_message([
8
+ "ebs - Manage EBS volumes in groups",
9
+ ["diff", "get a list of groups that have different definitions locally than in AWS (supplying the name of the group will diff only that group)"],
10
+ ["list", "list the groups defined in configuration"],
11
+ ["migrate", "create group configuration files that match the definitions in AWS"],
12
+ ["sync", "sync the local group definition with AWS (supplying the name of the group will sync only that group). Also creates volumes in a group"],
13
+ "instances - Manage EC2 instances",
14
+ ["diff", "get a list of instances that have different definitions locally than in AWS (supplying the name of the instance will diff only that instance)"],
15
+ ["list", "list the instances defined in configuration"],
16
+ ["migrate", "create instances configuration files that match the definitions in AWS"],
17
+ ["sync", "sync the local instance definition with AWS (supplying the name of the instance will sync only that instance)"],
18
+ ], indent: 1)
19
+ end
20
+
21
+ def self.manager_name
22
+ "ec2"
23
+ end
24
+
25
+ def self.valid_options
26
+ [["ebs", "instances"], ["diff", "list", "migrate", "sync"]]
27
+ end
28
+
29
+ def self.execute(arguments)
30
+ manager = if arguments[0] == "ebs"
31
+ require "ec2/managers/EbsManager"
32
+ Cumulus::EC2::EbsManager.new
33
+ elsif arguments[0] == "instances"
34
+ require "ec2/managers/InstanceManager"
35
+ Cumulus::EC2::InstanceManager.new
36
+ else
37
+ nil
38
+ end
39
+
40
+ if arguments[1] == "diff" and arguments.size == 3
41
+ manager.diff_one(arguments[2])
42
+ elsif arguments[1] == "sync" and arguments.size == 3
43
+ manager.sync_one(arguments[2])
44
+ else
45
+ manager.method(arguments[1]).call
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -187,7 +187,7 @@ module Cumulus
187
187
  if !group_diffs.empty?
188
188
  [group_name, EbsGroupDiff.modified(aws_config, group_config, group_diffs)]
189
189
  end
190
- end].reject { |k, v| v.nil? }
190
+ end.reject { |v| v.nil? }]
191
191
 
192
192
  ebs_changes = Common::ListChange.new(added_groups, removed_groups, changed_groups)
193
193
  if !ebs_changes.empty?
@@ -0,0 +1,40 @@
1
+ module Cumulus
2
+ module ELB
3
+ require "common/Commands"
4
+ class Commands < Cumulus::Common::Commands
5
+
6
+ def self.command_details
7
+ format_message [
8
+ ["diff", "print out differences between local configuration and AWS (supplying the name of the elb will diff only that elb)"],
9
+ ["list", "list the locally defined ELBs"],
10
+ ["sync", "sync local ELB definitions with AWS (supplying the name of the elb will sync only that elb)"],
11
+ ["migrate", "migrate AWS configuration to Cumulus"],
12
+ format_message([
13
+ ["default-policies", "migrate default ELB policies from AWS to Cumulus"],
14
+ ["elbs", "migrate the current ELB configuration from AWS to Cumulus"],
15
+ ], indent: 1),
16
+ ]
17
+ end
18
+
19
+ def self.manager
20
+ require "elb/manager/Manager"
21
+ Cumulus::ELB::Manager.new
22
+ end
23
+
24
+ def self.execute(arguments)
25
+ if arguments[0] == "migrate"
26
+ if arguments[1] == "default-policies"
27
+ manager.migrate_default_policies
28
+ elsif arguments[1] == "elbs"
29
+ manager.migrate_elbs
30
+ else
31
+ puts "Usage: cumulus elb migrate [default-policies|elbs]"
32
+ end
33
+ else
34
+ super(arguments)
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end