knife-cfn 0.1.10 → 0.1.11

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ed956b7bf7624fb88560a3aec0e5bc243b8a0169
4
+ data.tar.gz: 770c634481f6a69b773fa4602c521f0633b3664b
5
+ SHA512:
6
+ metadata.gz: d307cc10dd135f22c375397678232228331ae59cb9429dffc3b0a346169a0c6e7cc74029942ae9f067906715beafeaa5905de0093fecea7a222a7f8ac67dd92b
7
+ data.tar.gz: 67be89dad9b22c72d132529c8baefaaa9cf430305125e0415405bc959c6918e155d6d409f6358b5464d5747da878c4edeb0085426d5b5c1728a0534f5f17210c
@@ -1,4 +1,8 @@
1
1
  #
2
+ # Author:: Seth Chisamore (<schisamo@chef.io>)
3
+ # Copyright:: Copyright (c) 2011-2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
2
6
  # Licensed under the Apache License, Version 2.0 (the "License");
3
7
  # you may not use this file except in compliance with the License.
4
8
  # You may obtain a copy of the License at
@@ -30,6 +34,17 @@ class Chef
30
34
  require 'chef/json_compat'
31
35
  end
32
36
 
37
+ option :aws_credential_file,
38
+ :long => "--aws-credential-file FILE",
39
+ :description => "File containing AWS credentials as used by aws cmdline tools",
40
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_credential_file] = key }
41
+
42
+ option :aws_profile,
43
+ :long => "--aws-profile PROFILE",
44
+ :description => "AWS profile, from credential file, to use",
45
+ :default => 'default',
46
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_profile] = key }
47
+
33
48
  option :aws_access_key_id,
34
49
  :short => "-A ID",
35
50
  :long => "--aws-access-key-id KEY",
@@ -42,27 +57,45 @@ class Chef
42
57
  :description => "Your AWS API Secret Access Key",
43
58
  :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
44
59
 
60
+ option :aws_session_token,
61
+ :long => "--aws-session-token TOKEN",
62
+ :description => "Your AWS Session Token, for use with AWS STS Federation or Session Tokens",
63
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_session_token] = key }
64
+
45
65
  option :region,
46
66
  :long => "--region REGION",
47
67
  :description => "Your AWS region",
48
- :default => "us-east-1",
49
68
  :proc => Proc.new { |key| Chef::Config[:knife][:region] = key }
69
+
70
+ option :use_iam_profile,
71
+ :long => "--use-iam-profile",
72
+ :description => "Use IAM profile assigned to current machine",
73
+ :boolean => true,
74
+ :default => false,
75
+ :proc => Proc.new { |key| Chef::Config[:knife][:use_iam_profile] = key }
50
76
  end
51
77
  end
52
78
 
53
79
  def connection
80
+ connection_settings = {
81
+ # :provider => 'AWS',
82
+ :region => locate_config_value(:region)
83
+ }
84
+ if locate_config_value(:use_iam_profile)
85
+ connection_settings[:use_iam_profile] = true
86
+ else
87
+ connection_settings[:aws_access_key_id] = locate_config_value(:aws_access_key_id)
88
+ connection_settings[:aws_secret_access_key] = locate_config_value(:aws_secret_access_key)
89
+ connection_settings[:aws_session_token] = locate_config_value(:aws_session_token)
90
+ end
54
91
  @connection ||= begin
55
- connection = Fog::AWS::CloudFormation.new(
56
- :aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
57
- :aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
58
- :region => locate_config_value(:region)
59
- )
92
+ connection = Fog::AWS::CloudFormation.new(connection_settings)
60
93
  end
61
94
  end
62
95
 
63
96
  def locate_config_value(key)
64
97
  key = key.to_sym
65
- Chef::Config[:knife][key] || config[key]
98
+ config[key] || Chef::Config[:knife][key]
66
99
  end
67
100
 
68
101
  def msg_pair(label, value, color=:cyan)
@@ -71,23 +104,75 @@ class Chef
71
104
  end
72
105
  end
73
106
 
107
+ def is_image_windows?
108
+ image_info = connection.images.get(@server.image_id)
109
+ return image_info.platform == 'windows'
110
+ end
111
+
74
112
  def validate!(keys=[:aws_access_key_id, :aws_secret_access_key])
75
113
  errors = []
76
114
 
77
- keys.each do |k|
78
- pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
79
- if Chef::Config[:knife][k].nil?
80
- errors << "You did not provide a valid '#{pretty_key}' value."
115
+ unless locate_config_value(:use_iam_profile)
116
+ unless Chef::Config[:knife][:aws_credential_file].nil?
117
+ unless (Chef::Config[:knife].keys & [:aws_access_key_id, :aws_secret_access_key]).empty?
118
+ errors << "Either provide a credentials file or the access key and secret keys but not both."
119
+ end
120
+ # File format:
121
+ # AWSAccessKeyId=somethingsomethingdarkside
122
+ # AWSSecretKey=somethingsomethingcomplete
123
+ # OR
124
+ # [default]
125
+ # aws_access_key_id = somethingsomethingdarkside
126
+ # aws_secret_access_key = somethingsomethingdarkside
127
+
128
+ aws_creds = ini_parse(File.read(Chef::Config[:knife][:aws_credential_file]))
129
+ profile = Chef::Config[:knife][:aws_profile] || 'default'
130
+ entries = aws_creds.values.first.has_key?("AWSAccessKeyId") ? aws_creds.values.first : aws_creds[profile]
131
+
132
+ Chef::Config[:knife][:aws_access_key_id] = entries['AWSAccessKeyId'] || entries['aws_access_key_id']
133
+ Chef::Config[:knife][:aws_secret_access_key] = entries['AWSSecretKey'] || entries['aws_secret_access_key']
134
+ end
135
+
136
+ keys.each do |k|
137
+ pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
138
+ if Chef::Config[:knife][k].nil?
139
+ errors << "You did not provide a valid '#{pretty_key}' value."
140
+ end
81
141
  end
82
- end
83
142
 
84
- if errors.each{|e| ui.error(e)}.any?
85
- exit 1
143
+ if errors.each{|e| ui.error(e)}.any?
144
+ exit 1
145
+ end
86
146
  end
87
147
  end
88
148
 
89
149
  end
90
- end
91
- end
92
150
 
151
+ def iam_name_from_profile(profile)
152
+ # The IAM profile object only contains the name as part of the arn
153
+ if profile && profile.key?('arn')
154
+ name = profile['arn'].split('/')[-1]
155
+ end
156
+ name ||= ''
157
+ end
93
158
 
159
+ def ini_parse(file)
160
+ current_section = {}
161
+ map = {}
162
+ file.each_line do |line|
163
+ line = line.split(/^|\s;/).first # remove comments
164
+ section = line.match(/^\s*\[([^\[\]]+)\]\s*$/) unless line.nil?
165
+ if section
166
+ current_section = section[1]
167
+ elsif current_section
168
+ item = line.match(/^\s*(.+?)\s*=\s*(.+?)\s*$/) unless line.nil?
169
+ if item
170
+ map[current_section] ||= {}
171
+ map[current_section][item[1]] = item[2]
172
+ end
173
+ end
174
+ end
175
+ map
176
+ end
177
+ end
178
+ end
@@ -1,125 +1,125 @@
1
- #
2
- # Licensed under the Apache License, Version 2.0 (the "License");
3
- # you may not use this file except in compliance with the License.
4
- # You may obtain a copy of the License at
5
- #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
- #
8
- # Unless required by applicable law or agreed to in writing, software
9
- # distributed under the License is distributed on an "AS IS" BASIS,
10
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
- # See the License for the specific language governing permissions and
12
- # limitations under the License.
13
- #
14
-
15
- require 'chef/knife'
16
- require 'chef/knife/cfn_base'
17
-
18
- class Chef
19
- class Knife
20
- class CfnCreate < Chef::Knife::CfnBase
21
-
22
- deps do
23
- require 'fog'
24
- require 'readline'
25
- require 'chef/json_compat'
26
- require 'chef/knife/bootstrap'
27
- Chef::Knife::Bootstrap.load_deps
28
- end
29
-
30
- banner "knife cfn create <stack name> (options)"
31
-
32
- option :capabilities,
33
- :short => "-C CAPABILITY..",
34
- :long => "--capabilities CAPABILITY1,CAPABILITY2,CAPABILITY3..",
35
- :description => "The explicitly approved capabilities that may be used during this stack creation",
36
- :proc => Proc.new { |capabilities| capabilities.split(',') }
37
-
38
- option :disable_rollback,
39
- :short => "-d",
40
- :long => "--disable-rollback",
41
- :description => "Flag to disable rollback of created resources when failures are encountered during stack creation. The default value is 'false'",
42
- :proc => Proc.new { |d| Chef::Config[:knife][:disable_rollback] = "true" }
43
-
44
- option :template_file,
45
- :short => "-f TEMPLATE_FILE",
46
- :long => "--template-file TEMPLATE_FILE",
47
- :description => "Path to the file that contains the template",
48
- :proc => Proc.new { |f| Chef::Config[:knife][:template_file] = f }
49
-
50
- option :notification_arns,
51
- :short => "-n NOTIFICATION_ARN1,NOTIFICATION_ARN2,NOTIFICATION_ARN3..",
52
- :long => "--notification-arns VALUE1,VALUE2,VALUE3..",
53
- :description => "SNS ARNs to receive notification about the stack",
54
- :proc => Proc.new { |notification_arns| notification_arns.split(',') }
55
-
56
- option :parameters,
57
- :short => "-p 'key1=value1;key2=value2...'",
58
- :long => "--parameters 'key1=value1;key2=value2...'",
59
- :description => "Parameter values used to create the stack",
60
- :proc => Proc.new { |parameters| parameters.split(';') }
61
-
62
- option :timeout,
63
- :short => "-t TIMEOUT_VALUE",
64
- :long => "--timeout TIMEOUT_VALUE",
65
- :description => " Stack creation timeout in minutes",
66
- :proc => Proc.new { |t| Chef::Config[:knife][:timeout] = t }
67
-
68
- option :template_url,
69
- :short => "-u TEMPLATE_URL",
70
- :long => "--template-file TEMPLATE_URL",
71
- :description => "Path of the URL that contains the template. This must be a reference to a template in an S3 bucket in the same region that the stack will be created in",
72
- :proc => Proc.new { |u| Chef::Config[:knife][:template_url] = u }
73
-
74
- def run
75
- $stdout.sync = true
76
-
77
- validate!
78
-
79
- stack_name = @name_args[0]
80
-
81
- if stack_name.nil?
82
- show_usage
83
- ui.error("You must specify a stack name")
84
- exit 1
85
- end
86
-
87
- begin
88
- response = connection.create_stack(stack_name, create_create_def)
89
- rescue Excon::Errors::BadRequest => e
90
- i= e.response.body.index("<Message>")
91
- j = e.response.body.index("</Message>")
92
- if !i.nil? and !j.nil?
93
- ui.error(e.response.body[i+9,j-i-9])
94
- else
95
- print "\n#{e.response.body}"
96
- end
97
- exit 1
98
- else
99
- message = "Stack #{stack_name} creation started"
100
- print "\n#{ui.color(message, :green)}\n"
101
- end
102
- end
103
- end
104
-
105
- def create_create_def
106
- create_def = {}
107
- template_file = locate_config_value(:template_file)
108
- if template_file != nil and template_file != ""
109
- doc = File.open(template_file, 'rb') { |file| file.read }
110
- create_def['TemplateBody'] = doc
111
- end
112
- create_def['TemplateURL'] = locate_config_value(:template_url)
113
- create_def['Capabilities'] = locate_config_value(:capabilities)
114
- create_def['DisableRollback'] = locate_config_value(:disable_rollback)
115
- create_def['NotificationARNs'] = locate_config_value(:notification_arns)
116
- hashed_parameters={}
117
- parameters = locate_config_value(:parameters)
118
- parameters.map{ |t| key,val=t.split('='); hashed_parameters[key]=val} unless parameters.nil?
119
- create_def['Parameters'] = hashed_parameters
120
- create_def['TimeoutInMinutes'] = locate_config_value(:timeout)
121
- create_def
122
- end
123
-
124
- end
125
- end
1
+ #
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ #
14
+
15
+ require 'chef/knife'
16
+ require 'chef/knife/cfn_base'
17
+
18
+ class Chef
19
+ class Knife
20
+ class CfnCreate < Chef::Knife::CfnBase
21
+
22
+ deps do
23
+ require 'fog'
24
+ require 'readline'
25
+ require 'chef/json_compat'
26
+ require 'chef/knife/bootstrap'
27
+ Chef::Knife::Bootstrap.load_deps
28
+ end
29
+
30
+ banner "knife cfn create <stack name> (options)"
31
+
32
+ option :capabilities,
33
+ :short => "-C CAPABILITY..",
34
+ :long => "--capabilities CAPABILITY1,CAPABILITY2,CAPABILITY3..",
35
+ :description => "The explicitly approved capabilities that may be used during this stack creation",
36
+ :proc => Proc.new { |capabilities| capabilities.split(',') }
37
+
38
+ option :disable_rollback,
39
+ :short => "-d",
40
+ :long => "--disable-rollback",
41
+ :description => "Flag to disable rollback of created resources when failures are encountered during stack creation. The default value is 'false'",
42
+ :proc => Proc.new { |d| Chef::Config[:knife][:disable_rollback] = "true" }
43
+
44
+ option :template_file,
45
+ :short => "-f TEMPLATE_FILE",
46
+ :long => "--template-file TEMPLATE_FILE",
47
+ :description => "Path to the file that contains the template",
48
+ :proc => Proc.new { |f| Chef::Config[:knife][:template_file] = f }
49
+
50
+ option :notification_arns,
51
+ :short => "-n NOTIFICATION_ARN1,NOTIFICATION_ARN2,NOTIFICATION_ARN3..",
52
+ :long => "--notification-arns VALUE1,VALUE2,VALUE3..",
53
+ :description => "SNS ARNs to receive notification about the stack",
54
+ :proc => Proc.new { |notification_arns| notification_arns.split(',') }
55
+
56
+ option :parameters,
57
+ :short => "-p 'key1=value1;key2=value2...'",
58
+ :long => "--parameters 'key1=value1;key2=value2...'",
59
+ :description => "Parameter values used to create the stack",
60
+ :proc => Proc.new { |parameters| parameters.split(';') }
61
+
62
+ option :timeout,
63
+ :short => "-t TIMEOUT_VALUE",
64
+ :long => "--timeout TIMEOUT_VALUE",
65
+ :description => " Stack creation timeout in minutes",
66
+ :proc => Proc.new { |t| Chef::Config[:knife][:timeout] = t }
67
+
68
+ option :template_url,
69
+ :short => "-u TEMPLATE_URL",
70
+ :long => "--template-file TEMPLATE_URL",
71
+ :description => "Path of the URL that contains the template. This must be a reference to a template in an S3 bucket in the same region that the stack will be created in",
72
+ :proc => Proc.new { |u| Chef::Config[:knife][:template_url] = u }
73
+
74
+ def run
75
+ $stdout.sync = true
76
+
77
+ validate!
78
+
79
+ stack_name = @name_args[0]
80
+
81
+ if stack_name.nil?
82
+ show_usage
83
+ ui.error("You must specify a stack name")
84
+ exit 1
85
+ end
86
+
87
+ begin
88
+ response = connection.create_stack(stack_name, create_create_def)
89
+ rescue Excon::Errors::BadRequest => e
90
+ i= e.response.body.index("<Message>")
91
+ j = e.response.body.index("</Message>")
92
+ if !i.nil? and !j.nil?
93
+ ui.error(e.response.body[i+9,j-i-9])
94
+ else
95
+ print "\n#{e.response.body}"
96
+ end
97
+ exit 1
98
+ else
99
+ message = "Stack #{stack_name} creation started"
100
+ print "\n#{ui.color(message, :green)}\n"
101
+ end
102
+ end
103
+ end
104
+
105
+ def create_create_def
106
+ create_def = {}
107
+ template_file = locate_config_value(:template_file)
108
+ if template_file != nil and template_file != ""
109
+ doc = File.open(template_file, 'rb') { |file| file.read }
110
+ create_def['TemplateBody'] = doc
111
+ end
112
+ create_def['TemplateURL'] = locate_config_value(:template_url)
113
+ create_def['Capabilities'] = locate_config_value(:capabilities)
114
+ create_def['DisableRollback'] = locate_config_value(:disable_rollback)
115
+ create_def['NotificationARNs'] = locate_config_value(:notification_arns)
116
+ hashed_parameters={}
117
+ parameters = locate_config_value(:parameters)
118
+ parameters.map{ |t| key,val=t.split('='); hashed_parameters[key]=val} unless parameters.nil?
119
+ create_def['Parameters'] = hashed_parameters
120
+ create_def['TimeoutInMinutes'] = locate_config_value(:timeout)
121
+ create_def
122
+ end
123
+
124
+ end
125
+ end
@@ -26,28 +26,68 @@ class Chef
26
26
  require 'chef/knife/bootstrap'
27
27
  Chef::Knife::Bootstrap.load_deps
28
28
  end
29
-
29
+
30
30
  banner "knife cfn delete <stack name>"
31
-
31
+
32
+ option :aws_credential_file,
33
+ :long => "--aws-credential-file FILE",
34
+ :description => "File containing AWS credentials as used by aws cmdline tools",
35
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_credential_file] = key }
36
+
37
+ option :aws_profile,
38
+ :long => "--aws-profile PROFILE",
39
+ :description => "AWS profile, from credential file, to use",
40
+ :default => 'default',
41
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_profile] = key }
42
+
43
+ option :aws_access_key_id,
44
+ :short => "-A ID",
45
+ :long => "--aws-access-key-id KEY",
46
+ :description => "Your AWS Access Key ID",
47
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_access_key_id] = key }
48
+
49
+ option :aws_secret_access_key,
50
+ :short => "-K SECRET",
51
+ :long => "--aws-secret-access-key SECRET",
52
+ :description => "Your AWS API Secret Access Key",
53
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
54
+
55
+ option :aws_session_token,
56
+ :long => "--aws-session-token TOKEN",
57
+ :description => "Your AWS Session Token, for use with AWS STS Federation or Session Tokens",
58
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_session_token] = key }
59
+
60
+ option :region,
61
+ :long => "--region REGION",
62
+ :description => "Your AWS region",
63
+ :proc => Proc.new { |key| Chef::Config[:knife][:region] = key }
64
+
65
+ option :use_iam_profile,
66
+ :long => "--use-iam-profile",
67
+ :description => "Use IAM profile assigned to current machine",
68
+ :boolean => true,
69
+ :default => false,
70
+ :proc => Proc.new { |key| Chef::Config[:knife][:use_iam_profile] = key }
71
+
32
72
  def run
33
73
  $stdout.sync = true
34
74
 
35
75
  validate!
36
-
76
+
37
77
  stack_name = @name_args[0]
38
-
39
- if stack_name.nil?
40
- show_usage
41
- ui.error("You must specify a stack name")
42
- exit 1
43
- end
44
-
78
+
79
+ if stack_name.nil?
80
+ show_usage
81
+ ui.error("You must specify a stack name")
82
+ exit 1
83
+ end
84
+
45
85
  options = {}
46
86
  options['StackName'] = stack_name
47
87
  begin
48
88
  response = connection.describe_stacks(options)
49
- rescue Excon::Errors::BadRequest => e
50
- i= e.response.body.index("<Message>")
89
+ rescue Excon::Errors::BadRequest => e
90
+ i= e.response.body.index("<Message>")
51
91
  j = e.response.body.index("</Message>")
52
92
  if !i.nil? and !j.nil?
53
93
  ui.error(e.response.body[i+9,j-i-9])
@@ -55,15 +95,15 @@ class Chef
55
95
  print "\n#{e.response.body}"
56
96
  end
57
97
  exit 1
58
- end
59
-
60
- puts "\n"
61
- confirm("Do you really want to delete stack #{stack_name}")
62
-
98
+ end
99
+
100
+ puts "\n"
101
+ confirm("Do you really want to delete stack #{stack_name}")
102
+
63
103
  begin
64
104
  response = connection.delete_stack(stack_name)
65
105
  rescue Excon::Errors::BadRequest => e
66
- i= e.response.body.index("<Message>")
106
+ i= e.response.body.index("<Message>")
67
107
  j = e.response.body.index("</Message>")
68
108
  if !i.nil? and !j.nil?
69
109
  ui.error(e.response.body[i+9,j-i-9])
@@ -74,8 +114,8 @@ class Chef
74
114
  else
75
115
  message = "Stack #{stack_name} delete started"
76
116
  print "\n#{ui.color(message, :green)}"
77
- end
78
- end
79
- end
117
+ end
118
+ end
119
+ end
80
120
  end
81
121
  end