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.
- 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
|