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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eca2c2f560a3a801db6f64bad73bbf766e5884164a1bc56d23afebf29911417e
4
- data.tar.gz: 34897bd6689958ff49c4627ec08ae819364ba0a578b8deef0c92cb7219606a05
3
+ metadata.gz: e04afd6e4a0620bf4674a380606b26cb788fc4148ba1c5b3d6bd0f5b961a95f7
4
+ data.tar.gz: 5d9a506ba3e9c639e9ba356af39377c498146e3904f870a07018b7eacc1d5f0e
5
5
  SHA512:
6
- metadata.gz: 71989dcdb4ac810b423ce75684563a685dd04fed81cca49630fe01cd36bc2cc697df3b81ac262f97263fbdfb909c97a95bcdc66be25b6729373476e2e6e3df4f
7
- data.tar.gz: 10c20723124e241d81ed631cda7f7e9eb324acc428cb8c4bb3252c317a993ce6195d29ade245e1ff25145a817eb4d84ac3927dfffe2573305d76eb170c4e7955
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
- validate!
61
+ validate_aws_config!
62
62
  custom_warnings!
63
63
 
64
- server_list = [
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 = server_list.length
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
- servers.body["imagesSet"].each do |server|
82
- server["platform"] = find_server_platform(server["name"]) unless server["platform"]
83
-
84
- if locate_config_value(:platform) && locate_config_value(:search)
85
- locate_config_value(:search).downcase!
86
- if server["description"] && server["platform"] == locate_config_value(:platform) && server["description"].downcase.include?(locate_config_value(:search))
87
- server_list += get_server_list(server)
88
- end
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 get_server_list(server)
108
- server_list = []
109
- server_list << server["imageId"]
110
- server_list << server["platform"]
111
- server_list << server["architecture"]
112
- server_list << server["blockDeviceMapping"].first["volumeSize"].to_s
113
- server_list << server["name"].split(/\W+/).first
114
- server_list << server["description"]
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
@@ -27,26 +27,23 @@ class Chef
27
27
  includer.class_eval do
28
28
 
29
29
  deps do
30
- require "fog/aws"
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
- # @return [Fog::Compute]
83
- def connection
84
- connection_settings = {
85
- provider: "AWS",
86
- region: locate_config_value(:region),
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
- if locate_config_value(:use_iam_profile)
90
- connection_settings[:use_iam_profile] = true
91
- else
92
- connection_settings[:aws_access_key_id] = locate_config_value(:aws_access_key_id)
93
- connection_settings[:aws_secret_access_key] = locate_config_value(:aws_secret_access_key)
94
- connection_settings[:aws_session_token] = locate_config_value(:aws_session_token)
95
- end
96
- @connection ||= begin
97
- connection = Fog::Compute.new(connection_settings)
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
- config[key] || Chef::Config[:knife][key]
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
- image_info = connection.images.get(@server.image_id)
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 validate!(keys = [:aws_access_key_id, :aws_secret_access_key])
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
- profile = if locate_config_value(:aws_profile) == "default"
240
- "default"
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[profile]
247
- Chef::Config[:knife][:region] = aws_config[profile]["region"]
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 '#{profile}' is invalid."
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
- validate!
31
- custom_warnings!
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