knife-ec2 0.19.16 → 1.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.
@@ -35,6 +35,13 @@ class Chef
35
35
 
36
36
  attr_reader :server
37
37
 
38
+ option :dry_run,
39
+ long: "--dry-run",
40
+ short: "-n",
41
+ boolean: true,
42
+ default: false,
43
+ description: "Don't take action, only print what would happen."
44
+
38
45
  option :purge,
39
46
  short: "-P",
40
47
  long: "--purge",
@@ -54,61 +61,48 @@ class Chef
54
61
  # necessary to make them confirm two more times.
55
62
  def destroy_item(klass, name, type_name)
56
63
  object = klass.load(name)
57
- object.destroy
64
+ object.destroy unless config[:dry_run]
58
65
  ui.warn("Deleted #{type_name} #{name}")
59
66
  rescue Net::HTTPServerException
60
67
  ui.warn("Could not find a #{type_name} named #{name} to delete!")
61
68
  end
62
69
 
63
70
  def run
64
- validate!
65
- if @name_args.empty? && config[:chef_node_name]
66
- ui.info("no instance id is specific, trying to retrieve it from node name")
67
- instance_id = fetch_instance_id(config[:chef_node_name])
68
- @name_args << instance_id unless instance_id.nil?
69
- end
70
-
71
- @name_args.each do |instance_id|
72
-
73
- begin
74
- @server = connection.servers.get(instance_id)
75
-
76
- msg_pair("Instance ID", @server.id)
77
- msg_pair("Instance Name", @server.tags["Name"])
78
- msg_pair("Flavor", @server.flavor_id)
79
- msg_pair("Image", @server.image_id)
80
- msg_pair("Region", connection.instance_variable_get(:@region))
81
- msg_pair("Availability Zone", @server.availability_zone)
82
- msg_pair("Security Groups", @server.groups.join(", "))
83
- msg_pair("IAM Profile", iam_name_from_profile(@server.iam_instance_profile)) if @server.iam_instance_profile
84
- msg_pair("SSH Key", @server.key_name)
85
- msg_pair("Root Device Type", @server.root_device_type)
86
- msg_pair("Public DNS Name", @server.dns_name)
87
- msg_pair("Public IP Address", @server.public_ip_address)
88
- msg_pair("Private DNS Name", @server.private_dns_name)
89
- msg_pair("Private IP Address", @server.private_ip_address)
90
-
91
- puts "\n"
92
- confirm("Do you really want to delete this server")
93
-
94
- @server.destroy
95
-
96
- ui.warn("Deleted server #{@server.id}")
97
-
98
- if config[:purge]
99
- if config[:chef_node_name]
100
- thing_to_delete = config[:chef_node_name]
101
- else
102
- thing_to_delete = fetch_node_name(instance_id)
103
- end
104
- destroy_item(Chef::Node, thing_to_delete, "node")
105
- destroy_item(Chef::ApiClient, thing_to_delete, "client")
106
- else
107
- ui.warn("Corresponding node and client for the #{instance_id} server were not deleted and remain registered with the Chef Server")
108
- end
109
- rescue NoMethodError
110
- ui.error("Could not locate server '#{instance_id}'. Please verify it was provisioned in the '#{locate_config_value(:region)}' region.")
71
+ validate_aws_config!
72
+ validate_instances!
73
+
74
+ server_hashes.each do |h|
75
+ instance_id = h["instance_id"]
76
+ msg_pair("Instance ID", instance_id)
77
+ msg_pair("Instance Name", h["name"])
78
+ msg_pair("Flavor", h["instance_type"])
79
+ msg_pair("Image", h["image_id"])
80
+ msg_pair("Region", fetch_region)
81
+ msg_pair("Availability Zone", h["az"])
82
+ msg_pair("Security Groups", h["security_groups"])
83
+ msg_pair("IAM Profile", h["iam_instance_profile"])
84
+ msg_pair("SSH Key", h["key_name"])
85
+ msg_pair("Root Device Type", h["root_device_type"])
86
+ msg_pair("Public DNS Name", h["public_dns_name"])
87
+ msg_pair("Public IP Address", h["public_ip_address"])
88
+ msg_pair("Private DNS Name", h["private_dns_name"])
89
+ msg_pair("Private IP Address", h["private_ip_address"])
90
+
91
+ puts "\n"
92
+ confirm("Do you really want to delete this server")
93
+
94
+ delete_instance(instance_id) unless config[:dry_run]
95
+
96
+ ui.warn("Deleted server #{instance_id}")
97
+
98
+ if config[:purge]
99
+ node_name = config[:chef_node_name] || fetch_node_name(instance_id)
100
+ destroy_item(Chef::Node, node_name, "node")
101
+ destroy_item(Chef::ApiClient, node_name, "client")
102
+ else
103
+ ui.warn("Corresponding node and client for the #{instance_id} server were not deleted and remain registered with the Chef Server")
111
104
  end
105
+ puts "\n"
112
106
  end
113
107
  end
114
108
 
@@ -137,6 +131,63 @@ class Chef
137
131
  def query
138
132
  @query ||= Chef::Search::Query.new
139
133
  end
134
+
135
+ private
136
+
137
+ # @return [Array<Hash>]
138
+ def server_hashes
139
+ all_data = []
140
+
141
+ servers_list = ec2_connection.describe_instances({
142
+ instance_ids: @name_args,
143
+ })
144
+
145
+ servers_list.reservations.each do |i|
146
+ server_data = {}
147
+ %w{image_id instance_id instance_type key_name root_device_type public_ip_address private_ip_address private_dns_name public_dns_name}.each do |id|
148
+ server_data[id] = i.instances[0].send(id)
149
+ end
150
+
151
+ server_data["name"] = i.instances[0].tags[0].value
152
+ server_data["az"] = i.instances[0].placement.availability_zone
153
+ server_data["iam_instance_profile"] = ( i.instances[0].iam_instance_profile.nil? ? nil : i.instances[0].iam_instance_profile.arn[/instance-profile\/(.*)/] )
154
+ server_data["security_groups"] = i.instances[0].security_groups.map { |x| x.group_name }.join(", ")
155
+
156
+ all_data << server_data
157
+ end
158
+ all_data
159
+ end
160
+
161
+ # Delete the server instance
162
+ def delete_instance(instance_id)
163
+ return nil unless instance_id || instance_id.is_a?(String)
164
+ ec2_connection.terminate_instances({
165
+ instance_ids: [
166
+ instance_id,
167
+ ],
168
+ })
169
+ end
170
+
171
+ # If SERVER instance id not provided then check chef_name_tag and fetch the node
172
+ # And if the node contains instance id then add it to the name args
173
+ def validate_instances!
174
+ if @name_args.empty?
175
+ if config[:chef_node_name]
176
+ ui.info("No instance id is specified, trying to retrieve it from node name")
177
+ instance_id = fetch_instance_id(config[:chef_node_name])
178
+
179
+ if instance_id.nil?
180
+ ui.info("No instance id found.")
181
+ exit 1
182
+ else
183
+ @name_args << instance_id
184
+ end
185
+ else
186
+ ui.info("No instance id is specified.")
187
+ exit 1
188
+ end
189
+ end
190
+ end
140
191
  end
141
192
  end
142
193
  end
@@ -34,8 +34,15 @@ class Chef
34
34
  default: true,
35
35
  description: "Do not display name tag in output"
36
36
 
37
+ option :iamprofile,
38
+ short: "-i",
39
+ long: "--iam-profile",
40
+ boolean: true,
41
+ default: false,
42
+ description: "Show the iam profile"
43
+
37
44
  option :az,
38
- short: "-z",
45
+ short: "-a",
39
46
  long: "--availability-zone",
40
47
  boolean: true,
41
48
  default: false,
@@ -64,12 +71,24 @@ class Chef
64
71
  end
65
72
  end
66
73
 
74
+ # @return [Symbol]
75
+ def state_color(state)
76
+ case state
77
+ when "shutting-down", "terminated", "stopping", "stopped"
78
+ :red
79
+ when "pending"
80
+ :yellow
81
+ else
82
+ :green
83
+ end
84
+ end
85
+
67
86
  def run
68
87
  $stdout.sync = true
69
88
 
70
- validate!
89
+ validate_aws_config!
71
90
 
72
- server_list = [
91
+ servers_list = [
73
92
  ui.color("Instance ID", :bold),
74
93
 
75
94
  if config[:name]
@@ -94,65 +113,85 @@ class Chef
94
113
  end
95
114
  end,
96
115
 
97
- ui.color("IAM Profile", :bold),
116
+ if config[:iamprofile]
117
+ ui.color("IAM Profile", :bold)
118
+ end,
119
+
98
120
  ui.color("State", :bold)
99
121
  ].flatten.compact
100
122
 
101
- output_column_count = server_list.length
123
+ output_column_count = servers_list.length
102
124
 
103
125
  if !config[:region] && Chef::Config[:knife][:region].nil?
104
126
  ui.warn "No region was specified in knife.rb/config.rb or as an argument. The default region, us-east-1, will be used:"
105
127
  end
106
128
 
107
- servers = connection.servers
108
129
  if config[:format] == "summary"
109
- servers.each do |server|
110
- server_list << server.id.to_s
111
-
112
- if config[:name]
113
- server_list << server.tags["Name"].to_s
130
+ server_hashes.each do |v|
131
+ servers_list << v["instance_id"]
132
+ servers_list << v["name"] if config[:name]
133
+ servers_list << v["public_ip_address"]
134
+ servers_list << v["private_ip_address"]
135
+ servers_list << v["instance_type"]
136
+ servers_list << v["az"] if config[:az]
137
+ servers_list << v["image_id"]
138
+ servers_list << v["key_name"]
139
+ servers_list << v["security_groups"].join(",")
140
+ if config[:tags]
141
+ config[:tags].split(",").collect do |tag_name|
142
+ servers_list << v["tags"].find { |tag| tag == tag_name }
143
+ end
114
144
  end
145
+ servers_list << v["iam_instance_profile"].to_s if config[:iamprofile] # may be nil
146
+ servers_list << v["state"]
147
+ end
148
+ puts ui.list(servers_list, :uneven_columns_across, output_column_count)
149
+ else
150
+ output(format_for_display(server_hashes))
151
+ end
152
+ end
115
153
 
116
- server_list << server.public_ip_address.to_s
117
- server_list << server.private_ip_address.to_s
118
- server_list << server.flavor_id.to_s
154
+ private
119
155
 
120
- if config[:az]
121
- server_list << ui.color(
122
- server.availability_zone.to_s,
123
- azcolor(server.availability_zone.to_s)
124
- )
125
- end
156
+ # @return [Array<Hash>]
157
+ def server_hashes
158
+ all_data = []
159
+ ec2_connection.describe_instances.reservations.each do |i|
160
+ server_data = {}
161
+ %w{image_id instance_id instance_type key_name public_ip_address private_ip_address}.each do |id|
162
+ server_data[id] = i.instances[0].send(id)
163
+ end
126
164
 
127
- server_list << server.image_id.to_s
128
- server_list << server.key_name.to_s
129
- server_list << server.groups.join(", ")
165
+ # dig into tags struct
166
+ tags = extract_tags(i.instances[0].tags)
130
167
 
131
- if config[:tags]
132
- config[:tags].split(",").each do |tag_name|
133
- server_list << server.tags[tag_name].to_s
134
- end
135
- end
168
+ if config[:name]
169
+ server_data["name"] = tags[0]
170
+ end
136
171
 
137
- server_list << iam_name_from_profile(server.iam_instance_profile)
138
-
139
- server_list << begin
140
- state = server.state.to_s.downcase
141
- case state
142
- when "shutting-down", "terminated", "stopping", "stopped"
143
- ui.color(state, :red)
144
- when "pending"
145
- ui.color(state, :yellow)
146
- else
147
- ui.color(state, :green)
148
- end
149
- end
172
+ if config[:az]
173
+ server_data["az"] = ui.color(i.instances[0].placement.availability_zone, azcolor(i.instances[0].placement.availability_zone))
150
174
  end
151
175
 
152
- puts ui.list(server_list, :uneven_columns_across, output_column_count)
153
- else
154
- output(format_for_display(servers))
176
+ server_data["iam_instance_profile"] = ( i.instances[0].iam_instance_profile.nil? ? nil : i.instances[0].iam_instance_profile.arn[/instance-profile\/(.*)/] )
177
+
178
+ server_data["state"] = ui.color(i.instances[0].state.name, state_color(i.instances[0].state.name))
179
+
180
+ if config[:tags]
181
+ server_data["tags"] = tags
182
+ end
183
+
184
+ # dig into security_groups struct
185
+ server_data["security_groups"] = i.instances[0].security_groups.map { |x| x.group_name }
186
+
187
+ all_data << server_data
155
188
  end
189
+ all_data
190
+ end
191
+
192
+ # @return [Array<String>]
193
+ def extract_tags(tags_struct)
194
+ tags_struct.map { |x| x.value }
156
195
  end
157
196
  end
158
197
  end
@@ -0,0 +1,79 @@
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 Ec2SubnetList < Knife
24
+
25
+ include Knife::Ec2Base
26
+
27
+ banner "knife ec2 subnet list (options)"
28
+
29
+ def run
30
+ validate_aws_config!
31
+ custom_warnings!
32
+
33
+ subnet_list = [
34
+ ui.color("ID", :bold),
35
+ ui.color("State", :bold),
36
+ ui.color("CIDR Block", :bold),
37
+ ui.color("AZ", :bold),
38
+ ui.color("Available IPs", :bold),
39
+ ui.color("AZ Default?", :bold),
40
+ ui.color("Maps Public IP?", :bold),
41
+ ui.color("VPC ID", :bold)
42
+ ].flatten.compact
43
+
44
+ output_column_count = subnet_list.length
45
+
46
+ if config[:format] == "summary"
47
+ subnet_hash.each_pair do |_k, v|
48
+ subnet_list << v["subnet_id"]
49
+ subnet_list << v["state"]
50
+ subnet_list << v["cidr_block"]
51
+ subnet_list << v["availability_zone"]
52
+ subnet_list << v["available_ip_address_count"].to_s
53
+ subnet_list << (v["default_for_az"] ? "Yes" : "No")
54
+ subnet_list << (v["map_public_ip_on_launch"] ? "Yes" : "No")
55
+ subnet_list << v["vpc_id"]
56
+ end
57
+ puts ui.list(subnet_list, :uneven_columns_across, output_column_count)
58
+ else
59
+ output(format_for_display(subnet_hash))
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ # @return [Hash<Hash>]
66
+ def subnet_hash
67
+ all_data = {}
68
+ ec2_connection.describe_subnets.first.subnets.each do |s|
69
+ s_data = {}
70
+ %w{subnet_id availability_zone available_ip_address_count cidr_block default_for_az map_public_ip_on_launch state vpc_id}.each do |id|
71
+ s_data[id] = s.send(id)
72
+ end
73
+ all_data[s_data["subnet_id"]] = s_data
74
+ end
75
+ all_data
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,75 @@
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 Ec2VpcList < Knife
24
+
25
+ include Knife::Ec2Base
26
+
27
+ banner "knife ec2 vpc list (options)"
28
+
29
+ def run
30
+ validate_aws_config!
31
+ custom_warnings!
32
+
33
+ vpcs_list = [
34
+ ui.color("ID", :bold),
35
+ ui.color("State", :bold),
36
+ ui.color("CIDR Block", :bold),
37
+ ui.color("Instance Tenancy", :bold),
38
+ ui.color("DHCP Options ID", :bold),
39
+ ui.color("Default VPC?", :bold)
40
+ ].flatten.compact
41
+
42
+ output_column_count = vpcs_list.length
43
+
44
+ if config[:format] == "summary"
45
+ vpc_hash.each_pair do |_k, v|
46
+ vpcs_list << v["vpc_id"]
47
+ vpcs_list << v["state"]
48
+ vpcs_list << v["cidr_block"]
49
+ vpcs_list << v["instance_tenancy"]
50
+ vpcs_list << v["dhcp_options_id"]
51
+ vpcs_list << (v["is_default"] ? "Yes" : "No")
52
+ end
53
+ puts ui.list(vpcs_list, :uneven_columns_across, output_column_count)
54
+ else
55
+ output(format_for_display(vpc_hash))
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ # @return [Hash<Hash>]
62
+ def vpc_hash
63
+ all_data = {}
64
+ ec2_connection.describe_vpcs.first.vpcs.each do |v|
65
+ v_data = {}
66
+ %w{vpc_id cidr_block dhcp_options_id instance_tenancy is_default state}.each do |id|
67
+ v_data[id] = v.send(id)
68
+ end
69
+ all_data[v_data["vpc_id"]] = v_data
70
+ end
71
+ all_data
72
+ end
73
+ end
74
+ end
75
+ end