knife-rds 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +119 -0
- data/LICENSE +21 -0
- data/README.md +100 -0
- data/bin/console +20 -0
- data/knife-rds.gemspec +28 -0
- data/lib/chef/knife/rds_base.rb +75 -0
- data/lib/chef/knife/rds_base_data_bag.rb +145 -0
- data/lib/chef/knife/rds_create.rb +23 -0
- data/lib/chef/knife/rds_instance_delete.rb +29 -0
- data/lib/chef/knife/rds_instance_from_data_bag.rb +145 -0
- data/lib/chef/knife/rds_instance_list.rb +49 -0
- data/lib/chef/knife/rds_instance_restore_from_data_bag.rb +80 -0
- data/lib/chef/knife/rds_pg_from_data_bag.rb +159 -0
- data/lib/chef/knife/rds_pg_list.rb +39 -0
- data/lib/chef/knife/rds_sg_from_data_bag.rb +176 -0
- data/lib/chef/knife/rds_sg_list.rb +37 -0
- data/lib/knife-rds/version.rb +5 -0
- data/spec/spec_helper.rb +1 -0
- metadata +178 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'chef/knife/rds_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RdsCreate < Knife
|
6
|
+
|
7
|
+
'''
|
8
|
+
Create RDS instances.
|
9
|
+
'''
|
10
|
+
|
11
|
+
include Knife::RdsBase
|
12
|
+
|
13
|
+
banner 'knife rds create (args)'
|
14
|
+
|
15
|
+
def run
|
16
|
+
authenticate!
|
17
|
+
puts "Nothign created yet, sorry!"
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'chef/knife/rds_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RdsInstanceDelete < Knife
|
6
|
+
|
7
|
+
include Knife::RdsBase
|
8
|
+
|
9
|
+
banner 'knife rds instance delete INSTANCE_NAME'
|
10
|
+
|
11
|
+
def run
|
12
|
+
|
13
|
+
assert_name_args_at_least!(1, "Database identifier required.")
|
14
|
+
|
15
|
+
authenticate!
|
16
|
+
|
17
|
+
confirm("Are you sure you want to delete #{db_instance_id}")
|
18
|
+
|
19
|
+
rds.client.delete_db_instance({db_instance_identifier: db_instance_id, skip_final_snapshot: true})
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def db_instance_id
|
24
|
+
name_args.first
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'chef/knife/rds_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RdsInstanceFromDataBag < Knife
|
6
|
+
|
7
|
+
include Knife::RdsBase
|
8
|
+
include Knife::RdsBaseDataBag
|
9
|
+
|
10
|
+
option :data_bag_name,
|
11
|
+
:long => '--data-bag-name DATA_BAG_NAME',
|
12
|
+
:description => 'Name of databag containing RDS instances',
|
13
|
+
:default => 'rds_instances'
|
14
|
+
|
15
|
+
require_in_data_bag :engine, :master_username, :master_user_password, :allocated_storage, :db_instance_class
|
16
|
+
|
17
|
+
define_params *[
|
18
|
+
:preferred_backup_window, :engine, :master_username, :master_user_password, :db_instance_class,
|
19
|
+
:backup_retention_period, :allocated_storage, :db_security_groups, :multi_az, :auto_minor_version_upgrade,
|
20
|
+
:db_parameter_group_name, :preferred_maintenance_window, :availability_zone, :iops, :port
|
21
|
+
]
|
22
|
+
|
23
|
+
banner 'knife rds instance from data bag INSTANCE'
|
24
|
+
|
25
|
+
IGNORE_DIFF = [:master_user_password]
|
26
|
+
|
27
|
+
def run
|
28
|
+
|
29
|
+
assert_name_args_at_least!(1, "DB Instance identifier required")
|
30
|
+
|
31
|
+
assert_data_bag_item_valid!
|
32
|
+
|
33
|
+
authenticate!
|
34
|
+
|
35
|
+
if db_instance.nil?
|
36
|
+
ui.info("The instance #{db_instance_id} does not exit.")
|
37
|
+
confirm("Would you like to create it")
|
38
|
+
create_db_instance!
|
39
|
+
ui.info("Creating RDS instance #{db_instance_id}")
|
40
|
+
else
|
41
|
+
interrupt_unless_available!
|
42
|
+
ui.info("The instance #{db_instance_id} already exists")
|
43
|
+
confirm("Update with data bag values")
|
44
|
+
diff = generate_diff_with_data_bag
|
45
|
+
unless diff.empty?
|
46
|
+
confirm("Apply changes")
|
47
|
+
modify_db_instance!(diff)
|
48
|
+
else
|
49
|
+
ui.info("Your data bag does not contain any differences to instance.")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# Halt task is instance is not available
|
56
|
+
def interrupt_unless_available!
|
57
|
+
if ['creating', 'deleting'].include?(db_instance[:db_instance_status])
|
58
|
+
ui.warn("The instance is currently in the #{db_instance[:db_instance_status]} stage. Please wait until its finished.")
|
59
|
+
exit 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def skip_parameter?(parameter)
|
64
|
+
IGNORE_DIFF.include?(parameter)
|
65
|
+
end
|
66
|
+
def generate_diff_with_data_bag
|
67
|
+
diff = {}
|
68
|
+
ui.info "Comparing #{data_bag_item_name} with data bag settings..."
|
69
|
+
defined_params.each do |k, v|
|
70
|
+
if skip_parameter?(k)
|
71
|
+
ui.info("Will not update #{k}...")
|
72
|
+
next
|
73
|
+
end
|
74
|
+
if k == :db_parameter_group_name
|
75
|
+
# DB Parameter groups returned from AWS as hash. Needs to be handled separately
|
76
|
+
groups = db_instance[:db_parameter_groups].map { |g| g[:db_parameter_group_name] }
|
77
|
+
if groups.include?(v)
|
78
|
+
ui.info "DB Parameter groups unchanged."
|
79
|
+
else
|
80
|
+
diff[:db_parameter_group_name] = v
|
81
|
+
ui.info "* #{k} differs. data bag #{v} | instance #{groups}"
|
82
|
+
end
|
83
|
+
elsif k == :db_security_groups
|
84
|
+
groups = db_instance[:db_security_groups].map { |g| g[:db_security_group_name] }
|
85
|
+
if (groups - v).empty? && (v - groups).empty?
|
86
|
+
ui.info "DB Security groups unchanged"
|
87
|
+
else
|
88
|
+
ui.info "* #{k} differs. data bag #{v} | instance #{groups.join(', ')}"
|
89
|
+
diff[:db_security_groups] = v
|
90
|
+
end
|
91
|
+
else
|
92
|
+
if v != db_instance[k]
|
93
|
+
diff[k] = v
|
94
|
+
ui.info "* #{k} differs. data bag: #{v} | instance: #{db_instance[k]}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
diff
|
99
|
+
end
|
100
|
+
|
101
|
+
def modify_db_instance!(diff = {})
|
102
|
+
params = {db_instance_identifier: db_instance_id}.merge(diff)
|
103
|
+
rds.client.modify_db_instance(params)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Build API call using Data bag methods
|
107
|
+
def create_db_instance!
|
108
|
+
rds.client.create_db_instance(create_params)
|
109
|
+
end
|
110
|
+
|
111
|
+
def create_params
|
112
|
+
{ db_instance_identifier: db_instance_id }
|
113
|
+
.merge(defined_params)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Identifier for base data bag methods. Identifies the data bag item
|
117
|
+
#
|
118
|
+
# Returns stringbas
|
119
|
+
def data_bag_item_name
|
120
|
+
db_instance_id
|
121
|
+
end
|
122
|
+
|
123
|
+
# Database instance identifier
|
124
|
+
#
|
125
|
+
# Returns string
|
126
|
+
def db_instance_id
|
127
|
+
name_args.first
|
128
|
+
end
|
129
|
+
|
130
|
+
def db_instance
|
131
|
+
unless @db_instance
|
132
|
+
begin
|
133
|
+
@db_instance = rds.client.describe_db_instances(db_instance_identifier: db_instance_id)
|
134
|
+
@db_instance = @db_instance[:db_instances].first
|
135
|
+
rescue AWS::RDS::Errors::DBInstanceNotFound => e
|
136
|
+
@db_instance = nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
@db_instance
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'chef/knife/rds_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RdsInstanceList < Knife
|
6
|
+
|
7
|
+
'''
|
8
|
+
List RDS instances.
|
9
|
+
'''
|
10
|
+
|
11
|
+
# Key = attribute of DBInstance. Value = label to display
|
12
|
+
|
13
|
+
INSTANCE_INFO = {
|
14
|
+
id: 'ID',
|
15
|
+
status: 'Status',
|
16
|
+
db_name: 'DB Name',
|
17
|
+
engine: 'Engine',
|
18
|
+
engine_version: 'Engine Version',
|
19
|
+
iops: 'IOPS',
|
20
|
+
master_username: 'Master Username',
|
21
|
+
multi_az: 'Multi-AZ?',
|
22
|
+
endpoint_address: 'Endpoint',
|
23
|
+
created_at: 'Created At',
|
24
|
+
size: 'Size (GB)',
|
25
|
+
availability_zone_name: 'AZ',
|
26
|
+
db_instance_class: 'Instance Type',
|
27
|
+
|
28
|
+
}
|
29
|
+
|
30
|
+
include Knife::RdsBase
|
31
|
+
|
32
|
+
banner 'knife rds instance list (args)'
|
33
|
+
|
34
|
+
def run
|
35
|
+
authenticate!
|
36
|
+
rds.db_instances.each { |instance| present_instance(instance) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def present_instance(instance)
|
40
|
+
ui.info "-----"
|
41
|
+
INSTANCE_INFO.each do |k, v|
|
42
|
+
ui.info("#{v} - #{instance.send(k) || 'n/a'}")
|
43
|
+
end
|
44
|
+
ui.info '-----'
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'chef/knife/rds_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RdsInstanceRestoreFromDataBag < Knife
|
6
|
+
|
7
|
+
include Knife::RdsBase
|
8
|
+
include Knife::RdsBaseDataBag
|
9
|
+
|
10
|
+
option :data_bag_name,
|
11
|
+
:long => '--data-bag-name DATA_BAG_NAME',
|
12
|
+
:description => 'Name of databag containing RDS instances',
|
13
|
+
:default => 'rds_instances'
|
14
|
+
|
15
|
+
banner 'knife rds instance restore from data bag DB_INSTANCE_ID DB_SNAPSHOT_ID'
|
16
|
+
|
17
|
+
define_params *[
|
18
|
+
:port, :availability_zone, :multi_az, :auto_minor_version_upgrade,
|
19
|
+
:db_name, :license_model, :iops, :engine, :option_group_name, :parameter_group_name
|
20
|
+
]
|
21
|
+
|
22
|
+
def run
|
23
|
+
|
24
|
+
assert_name_args_at_least!(2)
|
25
|
+
|
26
|
+
assert_data_bag_item_valid!
|
27
|
+
|
28
|
+
authenticate!
|
29
|
+
|
30
|
+
ui.info("You are trying to restore snapshot #{db_snapshot_id} into instance #{db_instance_id}")
|
31
|
+
|
32
|
+
unless db_instance.nil?
|
33
|
+
ui.info("Instance #{db_instance_id} already exists!")
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
37
|
+
confirm("Restore Instance #{db_instance_id} from #{db_snapshot_id}")
|
38
|
+
|
39
|
+
restore_instance!
|
40
|
+
|
41
|
+
ui.info("Restore #{db_instance_id} from #{db_snapshot_id}")
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def restore_instance!
|
46
|
+
rds.client.restore_db_instance_from_db_snapshot(restore_params)
|
47
|
+
end
|
48
|
+
|
49
|
+
def restore_params
|
50
|
+
{ db_instance_identifier: db_instance_id, db_snapshot_identifier: db_snapshot_id }
|
51
|
+
.merge(defined_params)
|
52
|
+
end
|
53
|
+
|
54
|
+
def data_bag_item_name
|
55
|
+
db_instance_id
|
56
|
+
end
|
57
|
+
|
58
|
+
def db_instance_id
|
59
|
+
name_args.first
|
60
|
+
end
|
61
|
+
|
62
|
+
def db_snapshot_id
|
63
|
+
name_args.last
|
64
|
+
end
|
65
|
+
|
66
|
+
def db_instance
|
67
|
+
unless @db_instance
|
68
|
+
begin
|
69
|
+
@db_instance = rds.client.describe_db_instances(db_instance_identifier: db_instance_id)
|
70
|
+
@db_instance = @db_instance[:db_instances].first
|
71
|
+
rescue AWS::RDS::Errors::DBInstanceNotFound => e
|
72
|
+
@db_instance = nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
@db_instance
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'chef/knife/rds_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RdsPgFromDataBag < Knife
|
6
|
+
|
7
|
+
'''
|
8
|
+
Create or update a parameter group from data bag configuration.
|
9
|
+
The parameter group you are creating MUST have a data bag entry on your Chef server.
|
10
|
+
The parameter group will be updated if it exists, or created if it does not.
|
11
|
+
'''
|
12
|
+
|
13
|
+
include Knife::RdsBase
|
14
|
+
include Knife::RdsBaseDataBag
|
15
|
+
|
16
|
+
option :data_bag_name,
|
17
|
+
:long => '--data-bag-name DATA_BAG_NAME',
|
18
|
+
:description => 'Name of databag containing RDS instances',
|
19
|
+
:default => 'rds_parameter_groups'
|
20
|
+
|
21
|
+
option :apply_method,
|
22
|
+
:long => '--apply-method METHOD',
|
23
|
+
:description => 'Type of apply method (immediate or pending-reboot)',
|
24
|
+
:default => 'pending-reboot'
|
25
|
+
|
26
|
+
require_in_data_bag :id, :description, :db_parameter_group_family, :parameters
|
27
|
+
|
28
|
+
banner 'knife rds pg from data_bag PG_NAME (args)'
|
29
|
+
|
30
|
+
def run
|
31
|
+
|
32
|
+
assert_name_args_at_least!(1, "Parameter group name is required!")
|
33
|
+
|
34
|
+
assert_data_bag_item_valid!
|
35
|
+
|
36
|
+
assert_valid_apply_method!
|
37
|
+
|
38
|
+
authenticate!
|
39
|
+
|
40
|
+
# Check if exists parameter groups exists. If not, ask to create, and assign parameters
|
41
|
+
if db_parameter_group.nil?
|
42
|
+
ui.info("The parameter group #{db_parameter_group_name} does not exist.")
|
43
|
+
confirm("Would you like to create it")
|
44
|
+
create_db_parameter_group!
|
45
|
+
apply_method = 'immediate'
|
46
|
+
else
|
47
|
+
ui.info("The parameter group #{db_parameter_group_name} exists.")
|
48
|
+
confirm("Would you like to update #{db_parameter_group_name} with apply method #{config[:apply_method]}")
|
49
|
+
apply_method = config[:apply_method]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Reset user parameters no longer present in data bag
|
53
|
+
# Values will be rolled back to default
|
54
|
+
reset_parameters_from_db_parameter_group!(apply_method)
|
55
|
+
# Assign the parameters to the parameter group
|
56
|
+
assign_parameters_to_db_parameter_group!(apply_method)
|
57
|
+
|
58
|
+
# Success
|
59
|
+
ui.info("Assigned parameters to #{db_parameter_group_name}")
|
60
|
+
exit 0
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# Revoke custom db parameters from this group that are not present in data bag
|
65
|
+
# Check the parameters with soure = 'user' that exist on parameter group. If they are NOT in data bag, use reset API call
|
66
|
+
# to roll back to defaults
|
67
|
+
def reset_parameters_from_db_parameter_group!(apply_method)
|
68
|
+
# Params to reset
|
69
|
+
reset_params = []
|
70
|
+
# Get user sources
|
71
|
+
user_params = rds.client.describe_db_parameters(db_parameter_group_name: db_parameter_group_name, source: 'user')
|
72
|
+
user_params[:parameters].each do |p|
|
73
|
+
param_name = p[:parameter_name]
|
74
|
+
unless data_bag_item['parameters'].keys.include?(param_name)
|
75
|
+
ui.info "Revoking #{param_name}"
|
76
|
+
reset_params << { parameter_name: param_name, apply_method: apply_method }
|
77
|
+
if reset_params.length == 20
|
78
|
+
reset_parameters_with_params!(reset_params)
|
79
|
+
reset_params = []
|
80
|
+
end
|
81
|
+
else
|
82
|
+
ui.info "Keeping #{param_name}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
reset_parameters_with_params!(reset_params) unless reset_params.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
# Reset specified parameters in parameter group back to defaults
|
89
|
+
#
|
90
|
+
# params - Array of hashes of format {method: APPLY_METHOD, name: PARAMETER_NAME}
|
91
|
+
def reset_parameters_with_params!(params = [])
|
92
|
+
rds.client.reset_db_parameter_group(db_parameter_group_name: db_parameter_group_name, parameters: params)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Assigns all of data bag parameters to the parameter group
|
96
|
+
# AWS API support a maximum of 20 parameters updated in one call
|
97
|
+
def assign_parameters_to_db_parameter_group!(apply_method = 'pending-reboot')
|
98
|
+
params = []
|
99
|
+
data_bag_item['parameters'].each do |k, v|
|
100
|
+
ui.info("Assigning #{k} with value #{v}")
|
101
|
+
params << {parameter_name: k, parameter_value: v.to_s, apply_method: apply_method}
|
102
|
+
if params.length == 20
|
103
|
+
modify_db_parameter_group_with_params!(params)
|
104
|
+
params = []
|
105
|
+
end
|
106
|
+
end
|
107
|
+
modify_db_parameter_group_with_params!(params) unless params.empty?
|
108
|
+
end
|
109
|
+
|
110
|
+
# Modify the parameters of an existing RDS Parameter Group
|
111
|
+
def modify_db_parameter_group_with_params!(params = [])
|
112
|
+
begin
|
113
|
+
rds.client.modify_db_parameter_group(db_parameter_group_name: db_parameter_group_name, parameters: params)
|
114
|
+
rescue AWS::RDS::Errors::InvalidParameterValue => e
|
115
|
+
ui.error "Unable to update this batch parameter values. Please check that your parameters are correct."
|
116
|
+
ui.error "#{params.collect { |p| p[:parameter_name] }.join(', ') } not updated."
|
117
|
+
ui.error "#{e.message}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Create a new RDS Parameter group using the provideda data bag
|
122
|
+
def create_db_parameter_group!
|
123
|
+
rds.client.create_db_parameter_group(
|
124
|
+
db_parameter_group_name: db_parameter_group_name,
|
125
|
+
db_parameter_group_family: data_bag_item['db_parameter_group_family'],
|
126
|
+
description: data_bag_item['description']
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
# The name of the database parameter group, extracted from name arguments
|
131
|
+
#
|
132
|
+
# Returns string
|
133
|
+
def db_parameter_group_name
|
134
|
+
name_args.first
|
135
|
+
end
|
136
|
+
|
137
|
+
# For use with base data bag module.
|
138
|
+
def data_bag_item_name
|
139
|
+
db_parameter_group_name
|
140
|
+
end
|
141
|
+
|
142
|
+
# Load the DB Parameter Group resource from AWS using the API
|
143
|
+
#
|
144
|
+
# Returns AWS::RDS::DBParameterGroup or nil
|
145
|
+
def db_parameter_group
|
146
|
+
unless @db_parameter_group
|
147
|
+
begin
|
148
|
+
@db_parameter_group = rds.client.describe_db_parameter_groups(db_parameter_group_name: db_parameter_group_name)
|
149
|
+
rescue AWS::RDS::Errors::DBParameterGroupNotFound
|
150
|
+
@db_parameter_group = nil
|
151
|
+
end
|
152
|
+
@db_parameter_group
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|