knife-ec2 0.19.16 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/chef/knife/ec2_ami_list.rb +48 -37
- data/lib/chef/knife/ec2_base.rb +108 -36
- data/lib/chef/knife/ec2_eip_list.rb +88 -0
- data/lib/chef/knife/ec2_flavor_list.rb +3 -34
- data/lib/chef/knife/ec2_securitygroup_list.rb +68 -0
- data/lib/chef/knife/ec2_server_create.rb +429 -554
- data/lib/chef/knife/ec2_server_delete.rb +99 -48
- data/lib/chef/knife/ec2_server_list.rb +83 -44
- data/lib/chef/knife/ec2_subnet_list.rb +79 -0
- data/lib/chef/knife/ec2_vpc_list.rb +75 -0
- data/lib/chef/knife/s3_source.rb +27 -9
- data/lib/knife-ec2/version.rb +1 -1
- metadata +29 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e04afd6e4a0620bf4674a380606b26cb788fc4148ba1c5b3d6bd0f5b961a95f7
|
4
|
+
data.tar.gz: 5d9a506ba3e9c639e9ba356af39377c498146e3904f870a07018b7eacc1d5f0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9dab9ba5f3d204aab6d65534b3eb4e3a018007fa8917c44de439cc04a4bf8f745b6f9a14a96e2fcd4e811a6cc1f34df5eb50bfae825674886348cbbfaa738212
|
7
|
+
data.tar.gz: 9486a1aa93798a9aae585fc18e9888c93875a653a2d99bc2110ccd483446266daf16526960be44ad5acd53296ef9a931abc87b37c5af459097a6fdee70af85ec
|
@@ -58,10 +58,10 @@ class Chef
|
|
58
58
|
def run
|
59
59
|
$stdout.sync = true
|
60
60
|
|
61
|
-
|
61
|
+
validate_aws_config!
|
62
62
|
custom_warnings!
|
63
63
|
|
64
|
-
|
64
|
+
servers_list = [
|
65
65
|
ui.color("AMI ID", :bold),
|
66
66
|
ui.color("Platform", :bold),
|
67
67
|
ui.color("Architecture", :bold),
|
@@ -70,48 +70,59 @@ class Chef
|
|
70
70
|
ui.color("Description", :bold)
|
71
71
|
].flatten.compact
|
72
72
|
|
73
|
-
output_column_count =
|
74
|
-
begin
|
75
|
-
owner = locate_config_value(:owner) || "aws-marketplace"
|
76
|
-
servers = connection.describe_images({ "Owner" => "#{owner}" }) # aws-marketplace, microsoft
|
77
|
-
rescue Exception => api_error
|
78
|
-
raise api_error
|
79
|
-
end
|
73
|
+
output_column_count = servers_list.length
|
80
74
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
elsif locate_config_value(:platform)
|
90
|
-
if server["platform"] == locate_config_value(:platform)
|
91
|
-
server_list += get_server_list(server)
|
92
|
-
end
|
93
|
-
elsif locate_config_value(:search)
|
94
|
-
locate_config_value(:search).downcase!
|
95
|
-
if server["description"] && server["description"].downcase.include?(locate_config_value(:search))
|
96
|
-
server_list += get_server_list(server)
|
97
|
-
end
|
98
|
-
else
|
99
|
-
server_list += get_server_list(server)
|
75
|
+
if config[:format] == "summary"
|
76
|
+
ami_hashes.each_pair do |_k, v|
|
77
|
+
servers_list << v["image_id"]
|
78
|
+
servers_list << v["platform"]
|
79
|
+
servers_list << v["architecture"]
|
80
|
+
servers_list << v["size"]
|
81
|
+
servers_list << v["name"]
|
82
|
+
servers_list << v["description"]
|
100
83
|
end
|
84
|
+
puts ui.list(servers_list, :uneven_columns_across, output_column_count)
|
85
|
+
else
|
86
|
+
output(format_for_display(ami_hashes))
|
101
87
|
end
|
102
|
-
puts ui.list(server_list, :uneven_columns_across, output_column_count)
|
103
88
|
end
|
104
89
|
|
105
90
|
private
|
106
91
|
|
107
|
-
def
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
92
|
+
def ami_hashes
|
93
|
+
all_data = {}
|
94
|
+
ec2_connection.describe_images(image_params).images.each do |v|
|
95
|
+
v_data = {}
|
96
|
+
if locate_config_value(:search)
|
97
|
+
next unless v.description.downcase.include?(locate_config_value(:search).downcase)
|
98
|
+
end
|
99
|
+
|
100
|
+
%w{image_id platform description architecture}.each do |id|
|
101
|
+
v_data[id] = v.send(id)
|
102
|
+
end
|
103
|
+
|
104
|
+
v_data["name"] = v.name.split(/\W+/).first
|
105
|
+
v_data["size"] = v.block_device_mappings[0].ebs.volume_size.to_s
|
106
|
+
all_data[v_data["image_id"]] = v_data
|
107
|
+
end
|
108
|
+
all_data
|
109
|
+
end
|
110
|
+
|
111
|
+
def image_params
|
112
|
+
params = {}
|
113
|
+
owner = locate_config_value(:owner) || "aws-marketplace" # aws-marketplace, microsoft
|
114
|
+
params["owners"] = [owner.to_s]
|
115
|
+
|
116
|
+
filters = []
|
117
|
+
filters << { platform: locate_config_value(:platform) } if locate_config_value(:platform)
|
118
|
+
|
119
|
+
# TODO: Need to find substring to match in the description
|
120
|
+
# filters << { description: locate_config_value(:search) } if locate_config_value(:search)
|
121
|
+
|
122
|
+
if filters.length > 0
|
123
|
+
params["filters"] = filters
|
124
|
+
end
|
125
|
+
params
|
115
126
|
end
|
116
127
|
end
|
117
128
|
end
|
data/lib/chef/knife/ec2_base.rb
CHANGED
@@ -27,26 +27,23 @@ class Chef
|
|
27
27
|
includer.class_eval do
|
28
28
|
|
29
29
|
deps do
|
30
|
-
require "
|
30
|
+
require "aws-sdk-ec2"
|
31
31
|
require "chef/json_compat"
|
32
32
|
require "chef/util/path_helper"
|
33
33
|
end
|
34
34
|
|
35
35
|
option :aws_credential_file,
|
36
36
|
long: "--aws-credential-file FILE",
|
37
|
-
description: "File containing AWS credentials as used by the AWS Command Line Interface."
|
38
|
-
proc: Proc.new { |key| Chef::Config[:knife][:aws_credential_file] = key }
|
37
|
+
description: "File containing AWS credentials as used by the AWS Command Line Interface."
|
39
38
|
|
40
39
|
option :aws_config_file,
|
41
40
|
long: "--aws-config-file FILE",
|
42
|
-
description: "File containing AWS configurations as used by the AWS Command Line Interface."
|
43
|
-
proc: Proc.new { |key| Chef::Config[:knife][:aws_config_file] = key }
|
41
|
+
description: "File containing AWS configurations as used by the AWS Command Line Interface."
|
44
42
|
|
45
43
|
option :aws_profile,
|
46
44
|
long: "--aws-profile PROFILE",
|
47
45
|
description: "AWS profile, from AWS credential file and AWS config file, to use",
|
48
|
-
default: "default"
|
49
|
-
proc: Proc.new { |key| Chef::Config[:knife][:aws_profile] = key }
|
46
|
+
default: "default"
|
50
47
|
|
51
48
|
option :aws_access_key_id,
|
52
49
|
short: "-A ID",
|
@@ -68,7 +65,8 @@ class Chef
|
|
68
65
|
option :region,
|
69
66
|
long: "--region REGION",
|
70
67
|
description: "Your AWS region",
|
71
|
-
proc: Proc.new { |key| Chef::Config[:knife][:region] = key }
|
68
|
+
proc: Proc.new { |key| Chef::Config[:knife][:region] = key },
|
69
|
+
default: "us-east-1"
|
72
70
|
|
73
71
|
option :use_iam_profile,
|
74
72
|
long: "--use-iam-profile",
|
@@ -79,29 +77,102 @@ class Chef
|
|
79
77
|
end
|
80
78
|
end
|
81
79
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
80
|
+
def connection_string
|
81
|
+
conn = {}
|
82
|
+
conn[:region] = locate_config_value(:region)
|
83
|
+
conn[:credentials] =
|
84
|
+
if locate_config_value(:use_iam_profile)
|
85
|
+
Aws::InstanceProfileCredentials.new
|
86
|
+
else
|
87
|
+
Aws::Credentials.new(locate_config_value(:aws_access_key_id), locate_config_value(:aws_secret_access_key), locate_config_value(:aws_session_token))
|
88
|
+
end
|
89
|
+
conn
|
90
|
+
end
|
88
91
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
92
|
+
# @return [Aws::EC2::Client]
|
93
|
+
def ec2_connection
|
94
|
+
@ec2_connection ||= Aws::EC2::Client.new(connection_string)
|
95
|
+
end
|
96
|
+
|
97
|
+
def fetch_ami(image_id)
|
98
|
+
return {} unless image_id
|
99
|
+
ec2_connection.describe_images({
|
100
|
+
image_ids: [image_id],
|
101
|
+
}).images.first
|
102
|
+
end
|
103
|
+
|
104
|
+
def fetch_ec2_instance(instance_id)
|
105
|
+
instance = ec2_connection.describe_instances({
|
106
|
+
instance_ids: [
|
107
|
+
instance_id,
|
108
|
+
],
|
109
|
+
}).reservations[0]
|
110
|
+
normalize_server_data(server_hashes(instance))
|
111
|
+
end
|
112
|
+
|
113
|
+
def fetch_network_interfaces(nic_id)
|
114
|
+
ec2_connection.describe_network_interfaces({
|
115
|
+
network_interface_ids: [nic_id],
|
116
|
+
}).network_interfaces[0]
|
117
|
+
end
|
118
|
+
|
119
|
+
def fetch_password_data(server_id)
|
120
|
+
ec2_connection.get_password_data({
|
121
|
+
instance_id: server_id,
|
122
|
+
})
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [String]
|
126
|
+
def fetch_region
|
127
|
+
ec2_connection.instance_variable_get(:@config).region
|
128
|
+
end
|
129
|
+
|
130
|
+
def fetch_subnet(subnet_id)
|
131
|
+
ec2_connection.describe_subnets({
|
132
|
+
subnet_ids: [subnet_id],
|
133
|
+
}).subnets[0]
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [Hash]
|
137
|
+
def server_hashes(server_obj)
|
138
|
+
server_data = {}
|
139
|
+
%w{ebs_optimized image_id instance_id instance_type key_name platform public_dns_name public_ip_address private_dns_name private_ip_address root_device_type}.each do |id|
|
140
|
+
server_data[id] = server_obj.instances[0].send(id)
|
98
141
|
end
|
142
|
+
|
143
|
+
server_data["availability_zone"] = server_obj.instances[0].placement.availability_zone
|
144
|
+
server_data["groups"] = server_obj.groups.map { |grp| grp.name }
|
145
|
+
server_data["iam_instance_profile"] = ( server_obj.instances[0].iam_instance_profile.nil? ? nil : server_obj.instances[0].iam_instance_profile.arn[/instance-profile\/(.*)/] )
|
146
|
+
server_data["id"] = server_data["instance_id"]
|
147
|
+
|
148
|
+
tags = server_obj.instances[0].tags.map { |x| x.value }
|
149
|
+
server_data["name"] = tags[0]
|
150
|
+
server_data["placement_group"] = server_obj.instances[0].placement.group_name
|
151
|
+
server_data["security_groups"] = server_obj.instances[0].security_groups.map { |x| x.group_name }
|
152
|
+
server_data["security_group_ids"] = server_obj.instances[0].security_groups.map { |x| x.group_id }
|
153
|
+
server_data["state"] = server_obj.instances[0].state.name
|
154
|
+
server_data["subnet_id"] = server_obj.instances[0].network_interfaces[0].subnet_id
|
155
|
+
server_data["tags"] = tags
|
156
|
+
server_data["tenancy"] = server_obj.instances[0].placement.tenancy
|
157
|
+
server_data["volume_id"] = server_obj.instances[0].block_device_mappings[0]&.ebs&.volume_id
|
158
|
+
server_data["block_device_mappings"] = server_obj.instances[0].block_device_mappings
|
159
|
+
server_data
|
160
|
+
end
|
161
|
+
|
162
|
+
# @return [Struct]
|
163
|
+
def normalize_server_data(server_hashes)
|
164
|
+
require "ostruct"
|
165
|
+
OpenStruct.new(server_hashes)
|
99
166
|
end
|
100
167
|
|
101
168
|
# @return [String]
|
102
169
|
def locate_config_value(key)
|
103
170
|
key = key.to_sym
|
104
|
-
|
171
|
+
if defined?(config_value) # Inherited by bootstrap
|
172
|
+
config_value(key) || default_config[key]
|
173
|
+
else
|
174
|
+
config[key] || Chef::Config[:knife][key] || default_config[key]
|
175
|
+
end
|
105
176
|
end
|
106
177
|
|
107
178
|
def msg_pair(label, value, color = :cyan)
|
@@ -110,15 +181,19 @@ class Chef
|
|
110
181
|
end
|
111
182
|
end
|
112
183
|
|
184
|
+
def ami
|
185
|
+
@ami ||= fetch_ami(locate_config_value(:image))
|
186
|
+
end
|
187
|
+
|
188
|
+
# Platform value return for Windows AMIs; otherwise, it is blank.
|
113
189
|
# @return [Boolean]
|
114
190
|
def is_image_windows?
|
115
|
-
|
116
|
-
image_info.platform == "windows"
|
191
|
+
ami.platform == "windows"
|
117
192
|
end
|
118
193
|
|
119
194
|
# validate the config options that were passed since some of them cannot be used together
|
120
195
|
# also validate the aws configuration file contents if present
|
121
|
-
def
|
196
|
+
def validate_aws_config!(keys = [:aws_access_key_id, :aws_secret_access_key])
|
122
197
|
errors = [] # track all errors so we report on all of them
|
123
198
|
|
124
199
|
validate_aws_config_file! if locate_config_value(:aws_config_file)
|
@@ -157,7 +232,7 @@ class Chef
|
|
157
232
|
end
|
158
233
|
|
159
234
|
if locate_config_value(:owner)
|
160
|
-
unless ["self", "aws-marketplace", "microsoft"].include? (locate_config_value(:owner))
|
235
|
+
unless ["self", "aws-marketplace", "microsoft"].include? (locate_config_value(:owner)) # rubocop:disable Style/WordArray
|
161
236
|
raise ArgumentError, "Invalid owner: #{locate_config_value(:owner)}. Allowed owners are self, aws-marketplace or microsoft."
|
162
237
|
end
|
163
238
|
end
|
@@ -236,17 +311,14 @@ class Chef
|
|
236
311
|
raise ArgumentError, "The provided --aws_config_file (#{config_file}) cannot be found on disk." unless File.exist?(config_file)
|
237
312
|
|
238
313
|
aws_config = ini_parse(File.read(config_file))
|
239
|
-
|
240
|
-
|
241
|
-
else
|
242
|
-
"profile #{locate_config_value(:aws_profile)}"
|
243
|
-
end
|
314
|
+
profile_key = locate_config_value(:aws_profile)
|
315
|
+
profile_key = "profile #{profile_key}" if profile_key != "default"
|
244
316
|
|
245
317
|
unless aws_config.values.empty?
|
246
|
-
if aws_config[
|
247
|
-
Chef::Config[:knife][:region] = aws_config[
|
318
|
+
if aws_config[profile_key]
|
319
|
+
Chef::Config[:knife][:region] = aws_config[profile_key]["region"]
|
248
320
|
else
|
249
|
-
raise ArgumentError, "The provided --aws-profile '#{
|
321
|
+
raise ArgumentError, "The provided --aws-profile '#{profile_key}' is invalid."
|
250
322
|
end
|
251
323
|
end
|
252
324
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Tim Smith (<tsmith@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2018 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require "chef/knife/ec2_base"
|
20
|
+
|
21
|
+
class Chef
|
22
|
+
class Knife
|
23
|
+
class Ec2EniList < Knife
|
24
|
+
|
25
|
+
include Knife::Ec2Base
|
26
|
+
|
27
|
+
banner "knife ec2 eni list (options)"
|
28
|
+
|
29
|
+
def run
|
30
|
+
validate_aws_config!
|
31
|
+
custom_warnings!
|
32
|
+
|
33
|
+
eni_list = [
|
34
|
+
ui.color("ID", :bold),
|
35
|
+
ui.color("Status", :bold),
|
36
|
+
ui.color("AZ", :bold),
|
37
|
+
ui.color("Public IP", :bold),
|
38
|
+
ui.color("Private IPs", :bold),
|
39
|
+
ui.color("IPv6 IPs", :bold),
|
40
|
+
ui.color("Subnet ID", :bold),
|
41
|
+
ui.color("VPC ID", :bold)
|
42
|
+
].flatten.compact
|
43
|
+
|
44
|
+
output_column_count = eni_list.length
|
45
|
+
|
46
|
+
if config[:format] == "summary"
|
47
|
+
eni_hash.each_pair do |_k, v|
|
48
|
+
eni_list << v["network_interface_id"]
|
49
|
+
eni_list << v["status"]
|
50
|
+
eni_list << v["availability_zone"]
|
51
|
+
eni_list << v["public_ip"].to_s # to_s since it might be nil
|
52
|
+
eni_list << v["private_ips"].join(",")
|
53
|
+
eni_list << v["ipv_6_addresses"].join(",")
|
54
|
+
eni_list << v["subnet_id"]
|
55
|
+
eni_list << v["vpc_id"]
|
56
|
+
end
|
57
|
+
puts ui.list(eni_list, :uneven_columns_across, output_column_count)
|
58
|
+
else
|
59
|
+
output(format_for_display(eni_hash))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# build a hash of ENI we care about
|
66
|
+
# @return [Hash]
|
67
|
+
def eni_hash
|
68
|
+
all_data = {}
|
69
|
+
ec2_connection.describe_network_interfaces.first.network_interfaces.each do |eni|
|
70
|
+
eni_data = {}
|
71
|
+
%w{network_interface_id status mac_address availability_zone subnet_id vpc_id ipv_6_addresses}.each do |id|
|
72
|
+
eni_data[id] = eni.send(id)
|
73
|
+
end
|
74
|
+
|
75
|
+
# parse out the 1+ private IPs from the associations in the
|
76
|
+
# Aws::EC2::Types::NetworkInterfacePrivateIpAddress struct
|
77
|
+
eni_data["private_ips"] = eni.private_ip_addresses.map { |a| a["private_ip_address"] }
|
78
|
+
|
79
|
+
# grab the 1 public ip from the Aws::EC2::Types::NetworkInterfaceAssociation struct if it exists
|
80
|
+
eni_data["public_ip"] = ( eni.association.nil? ? nil : eni.association["public_ip"] )
|
81
|
+
|
82
|
+
all_data[eni_data["network_interface_id"]] = eni_data
|
83
|
+
end
|
84
|
+
all_data
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -24,42 +24,11 @@ class Chef
|
|
24
24
|
|
25
25
|
include Knife::Ec2Base
|
26
26
|
|
27
|
-
banner "knife ec2 flavor list (options)"
|
27
|
+
banner "knife ec2 flavor list (options) [DEPRECATED]"
|
28
28
|
|
29
29
|
def run
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
flavor_list = [
|
34
|
-
ui.color("ID", :bold),
|
35
|
-
ui.color("Name", :bold),
|
36
|
-
ui.color("Architecture", :bold),
|
37
|
-
ui.color("RAM", :bold),
|
38
|
-
ui.color("Disk", :bold),
|
39
|
-
ui.color("Cores", :bold)
|
40
|
-
].flatten.compact
|
41
|
-
|
42
|
-
output_column_count = flavor_list.length
|
43
|
-
|
44
|
-
begin
|
45
|
-
flavors = connection.flavors.sort_by(&:id)
|
46
|
-
rescue Exception => api_error
|
47
|
-
raise api_error
|
48
|
-
end
|
49
|
-
|
50
|
-
if config[:format] == "summary"
|
51
|
-
flavors.each do |flavor|
|
52
|
-
flavor_list << flavor.id.to_s
|
53
|
-
flavor_list << flavor.name
|
54
|
-
flavor_list << "#{flavor.bits}-bit"
|
55
|
-
flavor_list << "#{flavor.ram}"
|
56
|
-
flavor_list << "#{flavor.disk} GB"
|
57
|
-
flavor_list << flavor.cores.to_s
|
58
|
-
end
|
59
|
-
puts ui.list(flavor_list, :uneven_columns_across, output_column_count)
|
60
|
-
else
|
61
|
-
output(format_for_display(flavors))
|
62
|
-
end
|
30
|
+
ui.error("knife ec2 flavor list has been deprecated as this functionality is not provided by the AWS API the previous implementatin relied upon hardcoded values that were often incorrect. For an up to date list of instance types see https://www.ec2instances.info/")
|
31
|
+
exit 1
|
63
32
|
end
|
64
33
|
end
|
65
34
|
end
|