knife-rds 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,119 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ knife-rds (0.0.1)
5
+ aws-sdk
6
+ chef (>= 11.4.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (1.1.0)
12
+ aws-sdk (1.36.0)
13
+ json (~> 1.4)
14
+ nokogiri (>= 1.4.4)
15
+ uuidtools (~> 2.1)
16
+ chef (11.10.4)
17
+ chef-zero (~> 1.7, >= 1.7.2)
18
+ diff-lcs (~> 1.2, >= 1.2.4)
19
+ erubis (~> 2.7)
20
+ highline (~> 1.6, >= 1.6.9)
21
+ json (>= 1.4.4, <= 1.8.1)
22
+ mime-types (~> 1.16)
23
+ mixlib-authentication (~> 1.3)
24
+ mixlib-cli (~> 1.4)
25
+ mixlib-config (~> 2.0)
26
+ mixlib-log (~> 1.3)
27
+ mixlib-shellout (~> 1.3)
28
+ net-ssh (~> 2.6)
29
+ net-ssh-multi (~> 1.1)
30
+ ohai (~> 6.0)
31
+ pry (~> 0.9)
32
+ puma (~> 1.6)
33
+ rest-client (>= 1.0.4, < 1.7.0)
34
+ yajl-ruby (~> 1.1)
35
+ chef-zero (1.7.3)
36
+ hashie (~> 2.0)
37
+ json
38
+ mixlib-log (~> 1.3)
39
+ moneta (< 0.7.0)
40
+ rack
41
+ coderay (1.1.0)
42
+ diff-lcs (1.2.5)
43
+ erubis (2.7.0)
44
+ fakeweb (1.3.0)
45
+ hashie (2.0.5)
46
+ highline (1.6.21)
47
+ ipaddress (0.8.0)
48
+ json (1.8.1)
49
+ method_source (0.8.2)
50
+ mime-types (1.25.1)
51
+ mini_portile (0.5.2)
52
+ mixlib-authentication (1.3.0)
53
+ mixlib-log
54
+ mixlib-cli (1.4.0)
55
+ mixlib-config (2.1.0)
56
+ mixlib-log (1.6.0)
57
+ mixlib-shellout (1.3.0)
58
+ moneta (0.6.0)
59
+ net-ssh (2.8.0)
60
+ net-ssh-gateway (1.2.0)
61
+ net-ssh (>= 2.6.5)
62
+ net-ssh-multi (1.2.0)
63
+ net-ssh (>= 2.6.5)
64
+ net-ssh-gateway (>= 1.2.0)
65
+ nokogiri (1.6.1)
66
+ mini_portile (~> 0.5.0)
67
+ ohai (6.20.0)
68
+ ipaddress
69
+ mixlib-cli
70
+ mixlib-config
71
+ mixlib-log
72
+ mixlib-shellout
73
+ systemu (~> 2.5.2)
74
+ yajl-ruby
75
+ parser (2.1.7)
76
+ ast (~> 1.1)
77
+ slop (~> 3.4, >= 3.4.5)
78
+ powerpack (0.0.9)
79
+ pry (0.9.12.6)
80
+ coderay (~> 1.0)
81
+ method_source (~> 0.8)
82
+ slop (~> 3.4)
83
+ puma (1.6.3)
84
+ rack (~> 1.2)
85
+ rack (1.5.2)
86
+ rainbow (2.0.0)
87
+ rake (10.1.1)
88
+ rest-client (1.6.7)
89
+ mime-types (>= 1.16)
90
+ rspec (2.14.1)
91
+ rspec-core (~> 2.14.0)
92
+ rspec-expectations (~> 2.14.0)
93
+ rspec-mocks (~> 2.14.0)
94
+ rspec-core (2.14.8)
95
+ rspec-expectations (2.14.5)
96
+ diff-lcs (>= 1.1.3, < 2.0)
97
+ rspec-mocks (2.14.6)
98
+ rubocop (0.19.1)
99
+ json (>= 1.7.7, < 2)
100
+ parser (~> 2.1.7)
101
+ powerpack (~> 0.0.6)
102
+ rainbow (>= 1.99.1, < 3.0)
103
+ ruby-progressbar (~> 1.4)
104
+ ruby-progressbar (1.4.2)
105
+ slop (3.4.7)
106
+ systemu (2.5.2)
107
+ uuidtools (2.1.4)
108
+ yajl-ruby (1.2.0)
109
+
110
+ PLATFORMS
111
+ ruby
112
+
113
+ DEPENDENCIES
114
+ fakeweb
115
+ knife-rds!
116
+ pry
117
+ rake (~> 10.1)
118
+ rspec (~> 2.14)
119
+ rubocop
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Quandl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,100 @@
1
+ # knife-rds
2
+
3
+ ## Overview
4
+
5
+ A knife plugin for managing AWS RDS instances.
6
+
7
+ ** WORK IN PROGRESS. **
8
+
9
+ This project is very early, and should be taken as a proof of concept.
10
+
11
+ ## Motivation
12
+
13
+ * Integration of AWS resources with Chef intrastructure.
14
+
15
+ * Store configuration for security and parameter groups in Chef.
16
+
17
+ ## Commands (pending)
18
+
19
+ All commands use *your* EC2 keys, as configured in your Knife configuration. Your ability to interact with RDS resources is limited by your account access.
20
+
21
+
22
+ ### Instances
23
+
24
+ #### List
25
+
26
+ ```bash
27
+ knife rds list
28
+ ```
29
+
30
+ List available RDS Instances.
31
+
32
+ #### Modify
33
+
34
+ ```bash
35
+ knife rds instance from data bag (ITEM)
36
+ ```
37
+ Create or update an RDS instance using attributes stored in a data bag.
38
+
39
+ ### Security Groups
40
+
41
+ #### List
42
+
43
+ ```bash
44
+ knife sg list
45
+ ```
46
+
47
+ List created RDS security groups. Show name and description.
48
+
49
+ #### Modify
50
+
51
+ ```bash
52
+ knife rds sg from data bag SECURITY_GROUP_NAME
53
+ ```
54
+ Creates and/or updates an RDS security group and syncs the authorization levels with those specified in the data bag.
55
+
56
+ The command expects the following keys:
57
+
58
+ * description: (String) The description of the data bag. (Only required when security group is *created*).
59
+ * ec2_security_groups: (Array) A list of string EC2 security group names to whitelist for this RDS security group.
60
+ * ip_addressses: (Array) A list of IP addresses that should have access to this security group.
61
+
62
+ All EC2 security groups and IP addresses that exist in the data bag will be granted access to this security group.
63
+
64
+ Any EC2 securiy groups or IP addresses that previously had authorization, but no longer appear in the data bag, will have their authorization revoked.
65
+
66
+ The expected data bag name is 'rds_parameter_groups', but this can be overriden.
67
+
68
+ ### Parameter Groups
69
+
70
+ ####List
71
+
72
+ ```bash
73
+ knife pg list
74
+ ```
75
+
76
+ List created RDS parameter groups.
77
+
78
+
79
+ #### Modify
80
+
81
+ ```bash
82
+ knife pg from data bag PARAMETER_GROUP_NAME
83
+ ```
84
+
85
+ Creates and/or updates an RDS parameter group. Syncs user defined settings from data bag to AWS.
86
+
87
+ The command expects the following keys:
88
+
89
+ * description: (String) Parameter group descriptive text. (Only required when parameter group is *created*).
90
+ * db_parameter_group_family: (String) The parameter group family you group should inherit from. (Only required when parameter group is *created*).
91
+ * parameters: (Hash) Key,value combinations of parameters to be overriden.
92
+
93
+ All *valid parameters* in the data bag will be applied (via the 'apply method') to the data bag.
94
+
95
+ All *user supplied parameters* that exist in the parameter group, but are not present in the data bag, will be revoked (via the 'apply method').
96
+
97
+
98
+
99
+
100
+
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ require 'chef'
3
+ require 'pry'
4
+
5
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
6
+
7
+ Chef::Config.from_file File.expand_path(ENV.fetch('KNIFE_CONFIG', '~/.chef/knife.rb'))
8
+
9
+ Dir.glob(File.dirname(__FILE__) + "/../lib/chef/knife/*.rb").each do |task|
10
+ p = Pathname.new(task)
11
+ require "chef/knife/#{p.basename}"
12
+ end
13
+
14
+ begin
15
+ AWS.config(aws_access_key_id: Chef::Config[:knife][:aws_access_key_id], aws_secret_access_key: Chef::Config[:knife][:aws_secret_access_key])
16
+ rescue => e
17
+ puts e.message
18
+ end
19
+
20
+ binding.pry
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'knife-rds/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'knife-rds'
7
+ s.version = Knife::RDS::VERSION
8
+ s.authors = ['Jason Byck']
9
+ s.email = ['jbyck@quandl.com']
10
+ s.homepage = 'https://github.com/quandl/knife-rds'
11
+ s.summary = %q{Manage RDS}
12
+ s.description = s.summary
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+
17
+ s.add_dependency 'chef', '>= 11.4.0'
18
+ s.add_dependency 'aws-sdk'
19
+
20
+ s.add_development_dependency 'rspec', '~> 2.14'
21
+ s.add_development_dependency 'rake', '~> 10.1'
22
+ s.add_development_dependency 'fakeweb'
23
+ s.add_development_dependency 'pry'
24
+ s.add_development_dependency 'rubocop'
25
+
26
+ s.require_paths = ['lib']
27
+
28
+ end
@@ -0,0 +1,75 @@
1
+ require 'chef/knife'
2
+ require 'aws'
3
+
4
+ class Chef
5
+ class Knife
6
+ module RdsBase
7
+
8
+ '''
9
+ Base module to be used by All Knife::RDS commands
10
+ '''
11
+
12
+ def self.included(base)
13
+ base.class_eval do
14
+
15
+ option :aws_access_key_id,
16
+ :short => "-A ID",
17
+ :long => "--aws-access-key-id ID",
18
+ :description => "Your AWS Access Key ID",
19
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_access_key_id] = key }
20
+
21
+ option :aws_secret_access_key,
22
+ :short => "-K SECRET",
23
+ :long => "--aws-secret-access-key SECRET",
24
+ :description => "Your AWS API Secret Access Key",
25
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
26
+
27
+ end
28
+ end
29
+
30
+ APPLY_METHODS = ['immediate', 'pending-reboot'] unless defined?(APPLY_METHODS)
31
+
32
+ def assert_name_args_at_least!(num, error = 'Incorrect number of name args.')
33
+ unless name_args.size >= num
34
+ ui.fatal(error)
35
+ show_usage
36
+ exit 1
37
+ end
38
+ end
39
+
40
+ # Ensure user is authenticated before proceededing
41
+ #
42
+ # Raises ArgumentError
43
+ def authenticate!(required = [:aws_access_key_id, :aws_secret_access_key])
44
+ errors = []
45
+ required.each do |k|
46
+ if Chef::Config[:knife][k].nil?
47
+ errors << "A valid #{k} is required."
48
+ end
49
+ end
50
+ unless errors.empty?
51
+ errors.each { |e| ui.error(e) }
52
+ exit 1
53
+ end
54
+ connect!
55
+ end
56
+
57
+ # Assert user supplied apply method is valid
58
+ def assert_valid_apply_method!
59
+ unless APPLY_METHODS.include?(config[:apply_method])
60
+ ui.fatal("Unknown type of apply method #{config[:apply_method]}")
61
+ exit 1
62
+ end
63
+ end
64
+
65
+ def rds
66
+ AWS::RDS.new
67
+ end
68
+
69
+ def connect!
70
+ AWS.config(aws_access_key_id: config[:aws_access_key_id], aws_secret_access_key: config[:aws_secret_access_key])
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,145 @@
1
+ require 'chef/knife'
2
+ class Chef
3
+ class Knife
4
+ # Base module for interfacing with Data Bags
5
+ module RdsBaseDataBag
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+ # ClassMethods
10
+ module ClassMethods
11
+ @@required_data_bag_options ||= {}
12
+ @@data_bag_params ||= {}
13
+
14
+ # Safe string name for the task
15
+ def task_name
16
+ name.to_s.downcase.gsub(/:/, '')
17
+ end
18
+
19
+ def define_params(*params)
20
+ include_params_for(params, @@data_bag_params)
21
+ end
22
+
23
+ def require_in_data_bag(*params)
24
+ include_params_for(params, @@required_data_bag_options)
25
+ end
26
+
27
+ # Helper method for building attribute list for the task
28
+ #
29
+ # params - Array of parameter names
30
+ # attribute - Class variable
31
+ def include_params_for(params, attribute)
32
+ attribute[task_name] ||= []
33
+ Array(params).flatten.each do |param|
34
+ attribute[task_name] << param.to_s
35
+ end
36
+ end
37
+
38
+ # Return array of options that must exist in data bag
39
+ def required_data_bag_options
40
+ if @@required_data_bag_options[task_name].nil?
41
+ []
42
+ else
43
+ @@required_data_bag_options[task_name].uniq
44
+ end
45
+ end
46
+
47
+ # Helper method for returning the parameters defined for type
48
+ #
49
+ # type - String type of parameter (ex. :string or :integer)
50
+ #
51
+ # Returns array
52
+ def defined_params
53
+ if @@data_bag_params[task_name].nil?
54
+ []
55
+ else
56
+ @@data_bag_params[task_name].uniq
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ # Call the class method for the vale
63
+ def required_data_bag_options
64
+ self.class.required_data_bag_options
65
+ end
66
+
67
+ # Build a hash containing only the type (integer, string) parameters that exist in
68
+ # the data bag, coerced to their expected type
69
+ #
70
+ # type - Sym, name of type (:integer, :string)
71
+ #
72
+ # Returns hash
73
+ def defined_params
74
+ values = {}
75
+ self.class.defined_params.each do |k|
76
+ v = data_bag_item[k]
77
+ next if v.nil?
78
+ values[k.to_sym] = v
79
+ end
80
+ values
81
+ end
82
+
83
+ # Data bag assertion. Calls all boilerplate data bag assertions
84
+ def assert_data_bag_item_valid!
85
+ assert_data_bag_exists!
86
+ assert_data_bag_item_exists!
87
+ assert_required_data_bag_options_present!
88
+ end
89
+
90
+ # Report error and quit unless defined required parameters exits in data bag item
91
+ def assert_required_data_bag_options_present!
92
+ required_data_bag_options.each do |opt|
93
+ unless data_bag_item[opt]
94
+ ui.fatal("Missing option #{opt} for #{data_bag_item_name}")
95
+ exit 1
96
+ end
97
+ end
98
+ end
99
+
100
+ # Report error and quit unless data bag item exists
101
+ def assert_data_bag_item_exists!
102
+ if data_bag_item.nil?
103
+ db_item_name = "#{config[:data_bag_name]}/#{data_bag_item_name}"
104
+ ui.fatal("Cannot load data bag item #{db_item_name}")
105
+ exit 1
106
+ end
107
+ end
108
+
109
+ # Report error and quit unless data bag exists
110
+ def assert_data_bag_exists!
111
+ unless data_bag_exists?
112
+ ui.fatal("Data bag #{config[:data_bag_name]} must exist.")
113
+ exit 1
114
+ end
115
+ end
116
+
117
+ # Check if the configured RDS data bag exists on the Chef Server
118
+ #
119
+ # Returns boolean
120
+ def data_bag_exists?
121
+ begin
122
+ Chef::DataBag.load(config[:data_bag_name])
123
+ rescue Net::HTTPServerException
124
+ return false
125
+ end
126
+ true
127
+ end
128
+
129
+ # The data bag item. Uses the option name data_bag_item, and the parameter group name argument
130
+ # to load the data bag item from the server
131
+ #
132
+ # Returns Chef::DataBagItem or nil
133
+ def data_bag_item
134
+ unless @data_bag_item
135
+ begin
136
+ @data_bag_item = Chef::DataBagItem.load(config[:data_bag_name], data_bag_item_name)
137
+ rescue Net::HTTPServerException
138
+ @data_bag_item = nil
139
+ end
140
+ end
141
+ @data_bag_item
142
+ end
143
+ end
144
+ end
145
+ end