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.
@@ -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