lucid-cumulus 0.11.2 → 0.11.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile +1 -0
- data/Gemfile.lock +19 -4
- data/README.md +2 -0
- data/bin/cumulus +22 -546
- data/lib/autoscaling/Commands.rb +20 -0
- data/lib/aws_extensions/s3/BucketPolicy.rb +22 -4
- data/lib/cloudfront/Commands.rb +51 -0
- data/lib/common/BaseLoader.rb +6 -4
- data/lib/common/Commands.rb +131 -0
- data/lib/conf/Configuration.rb +23 -6
- data/lib/ec2/Commands.rb +51 -0
- data/lib/ec2/models/InstanceConfig.rb +1 -1
- data/lib/elb/Commands.rb +40 -0
- data/lib/iam/Commands.rb +49 -0
- data/lib/iam/models/ResourceWithPolicy.rb +10 -3
- data/lib/iam/models/StatementConfig.rb +2 -2
- data/lib/kinesis/Commands.rb +20 -0
- data/lib/route53/Commands.rb +20 -0
- data/lib/s3/Commands.rb +20 -0
- data/lib/s3/loader/Loader.rb +2 -1
- data/lib/security/Commands.rb +24 -0
- data/lib/sqs/Commands.rb +34 -0
- data/lib/sqs/SQS.rb +6 -0
- data/lib/sqs/loader/Loader.rb +9 -4
- data/lib/sqs/manager/Manager.rb +6 -7
- data/lib/vpc/Commands.rb +43 -0
- data/lucid-cumulus.gemspec +2 -1
- data/rakefile.rb +7 -2
- data/spec/mocks/ClientSpy.rb +43 -0
- data/spec/mocks/MockedConfiguration.rb +61 -0
- data/spec/mocks/MockedLoader.rb +37 -0
- data/spec/mocks/MockedStatusCodes.rb +17 -0
- data/spec/rspec_config.rb +5 -0
- data/spec/sqs/SQSUtil.rb +231 -0
- data/spec/sqs/SingleChangeTest.rb +101 -0
- data/spec/sqs/diff_spec.rb +152 -0
- data/spec/sqs/sync_spec.rb +137 -0
- data/spec/util/DeepMerge.rb +25 -0
- data/spec/util/ManagerUtil.rb +12 -0
- metadata +39 -3
- data/cumulus +0 -2
@@ -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.
|
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
|
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
|
data/lib/common/BaseLoader.rb
CHANGED
@@ -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.
|
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
|
54
|
-
|
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
|
data/lib/conf/Configuration.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/ec2/Commands.rb
ADDED
@@ -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
|
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?
|
data/lib/elb/Commands.rb
ADDED
@@ -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
|