knife-rds 0.0.1
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.
- 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
|