knife-cfn 0.1.0

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.
@@ -0,0 +1,93 @@
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
+
17
+ class Chef
18
+ class Knife
19
+ class CfnBase < Knife
20
+
21
+ # :nodoc:
22
+ # Would prefer to do this in a rational way, but can't be done b/c of
23
+ # Mixlib::CLI's design :(
24
+ def self.included(includer)
25
+ includer.class_eval do
26
+
27
+ deps do
28
+ require 'fog'
29
+ require 'readline'
30
+ require 'chef/json_compat'
31
+ end
32
+
33
+ option :aws_access_key_id,
34
+ :short => "-A ID",
35
+ :long => "--aws-access-key-id KEY",
36
+ :description => "Your AWS Access Key ID",
37
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_access_key_id] = key }
38
+
39
+ option :aws_secret_access_key,
40
+ :short => "-K SECRET",
41
+ :long => "--aws-secret-access-key SECRET",
42
+ :description => "Your AWS API Secret Access Key",
43
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
44
+
45
+ option :region,
46
+ :long => "--region REGION",
47
+ :description => "Your AWS region",
48
+ :default => "us-east-1",
49
+ :proc => Proc.new { |key| Chef::Config[:knife][:region] = key }
50
+ end
51
+ end
52
+
53
+ def connection
54
+ @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
+ )
60
+ end
61
+ end
62
+
63
+ def locate_config_value(key)
64
+ key = key.to_sym
65
+ Chef::Config[:knife][key] || config[key]
66
+ end
67
+
68
+ def msg_pair(label, value, color=:cyan)
69
+ if value && !value.to_s.empty?
70
+ puts "#{ui.color(label, color)}: #{value}"
71
+ end
72
+ end
73
+
74
+ def validate!(keys=[:aws_access_key_id, :aws_secret_access_key])
75
+ errors = []
76
+
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."
81
+ end
82
+ end
83
+
84
+ if errors.each{|e| ui.error(e)}.any?
85
+ exit 1
86
+ end
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+
93
+
@@ -0,0 +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)}"
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
@@ -0,0 +1,81 @@
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 CfnDelete < 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 delete <stack name>"
31
+
32
+ def run
33
+ $stdout.sync = true
34
+
35
+ validate!
36
+
37
+ 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
+
45
+ options = {}
46
+ options['StackName'] = stack_name
47
+ begin
48
+ response = connection.describe_stacks(options)
49
+ rescue Excon::Errors::BadRequest => e
50
+ i= e.response.body.index("<Message>")
51
+ j = e.response.body.index("</Message>")
52
+ if !i.nil? and !j.nil?
53
+ ui.error(e.response.body[i+9,j-i-9])
54
+ else
55
+ print "\n#{e.response.body}"
56
+ end
57
+ exit 1
58
+ end
59
+
60
+ puts "\n"
61
+ confirm("Do you really want to delete stack #{stack_name}")
62
+
63
+ begin
64
+ response = connection.delete_stack(stack_name)
65
+ rescue Excon::Errors::BadRequest => e
66
+ i= e.response.body.index("<Message>")
67
+ j = e.response.body.index("</Message>")
68
+ if !i.nil? and !j.nil?
69
+ ui.error(e.response.body[i+9,j-i-9])
70
+ else
71
+ print "\n#{e.response.body}"
72
+ end
73
+ exit 1
74
+ else
75
+ message = "Stack #{stack_name} delete started"
76
+ print "\n#{ui.color(message, :green)}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,84 @@
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 CfnDescribe < 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 describe <stack name>"
31
+
32
+ def run
33
+ $stdout.sync = true
34
+
35
+ validate!
36
+
37
+ 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
+
45
+ stack_list = [
46
+ ui.color('Stack ID', :bold),
47
+ ui.color('Status', :bold),
48
+ ui.color('Creation Time', :bold),
49
+ ui.color('Disable Rollback', :bold)
50
+ ]
51
+
52
+ @name_args.each do |stack_name|
53
+ options = {}
54
+ data = Array.new
55
+ options['StackName'] = stack_name
56
+ begin
57
+ response = connection.describe_stacks(options)
58
+ data = response.body['Stacks']
59
+ rescue Excon::Errors::BadRequest => e
60
+ i= e.response.body.index("<Message>")
61
+ j = e.response.body.index("</Message>")
62
+ if !i.nil? and !j.nil?
63
+ ui.error(e.response.body[i+9,j-i-9])
64
+ else
65
+ print "\n#{e.response.body}"
66
+ end
67
+ exit 1
68
+ else
69
+ data.each do |stack|
70
+ stack_list << stack['StackId']
71
+ stack_list << stack['StackStatus']
72
+ stack_list << stack['CreationTime'].to_s
73
+ stack_list << stack['DisableRollback'].to_s
74
+ end
75
+ puts ui.list(stack_list, :uneven_columns_across, 4)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ #end
82
+ end
83
+
84
+
@@ -0,0 +1,88 @@
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 CfnEvents < 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 events <stack name>"
31
+
32
+ def run
33
+ $stdout.sync = true
34
+
35
+ validate!
36
+
37
+ 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
+
45
+ events_list = [
46
+ ui.color('Event ID', :bold),
47
+ ui.color('Stack ID', :bold),
48
+ ui.color('Logical Resource Id', :bold),
49
+ ui.color('Physical Resource Id', :bold),
50
+ ui.color('Resource Type', :bold),
51
+ ui.color('Timestamp', :bold),
52
+ ui.color('Resource Status', :bold),
53
+ ui.color('Resource Status Reason', :bold)
54
+ ]
55
+ @name_args.each do |stack_name|
56
+ data = Array.new
57
+ begin
58
+ response = connection.describe_stack_events(stack_name)
59
+ data = response.body['StackEvents']
60
+ rescue Excon::Errors::BadRequest => e
61
+ i= e.response.body.index("<Message>")
62
+ j = e.response.body.index("</Message>")
63
+ if !i.nil? and !j.nil?
64
+ ui.error(e.response.body[i+9,j-i-9])
65
+ else
66
+ print "\n#{e.response.body}"
67
+ end
68
+ exit 1
69
+ else
70
+ data.each do |event|
71
+ events_list << event['EventId']
72
+ events_list << event['StackId']
73
+ events_list << event['LogicalResourceId']
74
+ events_list << event['PhysicalResourceId']
75
+ events_list << event['ResourceType']
76
+ events_list << event['Timestamp'].to_s
77
+ events_list << event['ResourceStatus']
78
+ events_list << event['ResourceStatusReason'].to_s
79
+ end
80
+ puts ui.list(events_list, :uneven_columns_across, 8)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+
@@ -0,0 +1,80 @@
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 CfnValidate < 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 validate (options)"
31
+
32
+ option :template_file,
33
+ :short => "-f TEMPLATE_FILE",
34
+ :long => "--template-file TEMPLATE_FILE",
35
+ :description => "Path to the file that contains the template",
36
+ :proc => Proc.new { |f| Chef::Config[:knife][:template_file] = f }
37
+
38
+ option :template_url,
39
+ :short => "-u TEMPLATE_URL",
40
+ :long => "--template-file TEMPLATE_URL",
41
+ :description => "Path to the URL that contains the template",
42
+ :proc => Proc.new { |u| Chef::Config[:knife][:template_url] = u }
43
+
44
+
45
+ def run
46
+ $stdout.sync = true
47
+
48
+ validate!
49
+
50
+ begin
51
+ response = connection.validate_template(create_validate_def)
52
+ rescue Excon::Errors::BadRequest => e
53
+ i= e.response.body.index("<Message>")
54
+ j = e.response.body.index("</Message>")
55
+ if !i.nil? and !j.nil?
56
+ ui.error(e.response.body[i+9,j-i-9])
57
+ else
58
+ print "\n#{e.response.body}"
59
+ end
60
+ exit 1
61
+ else
62
+ print "\n#{ui.color("Template validated successfully", :green)}"
63
+ end
64
+ end
65
+
66
+ def create_validate_def
67
+ validate_def = {}
68
+ template_file = locate_config_value(:template_file)
69
+ if template_file != nil and template_file != ""
70
+ doc = File.open(template_file, 'rb') { |file| file.read }
71
+ validate_def['TemplateBody'] = doc
72
+ else
73
+ validate_def['TemplateURL'] = locate_config_value(:template_url)
74
+ end
75
+ validate_def
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ module KnifeCfn
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-cfn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Neill Turner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-16 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fog
16
+ requirement: &27370104 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *27370104
25
+ - !ruby/object:Gem::Dependency
26
+ name: chef
27
+ requirement: &27369828 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '0.10'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *27369828
36
+ description: CloudFormation Support for Chef's Knife Command
37
+ email: neillwturner@gmail.com
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - lib/chef/knife/cfn_base.rb
43
+ - lib/chef/knife/cfn_create.rb
44
+ - lib/chef/knife/cfn_delete.rb
45
+ - lib/chef/knife/cfn_describe.rb
46
+ - lib/chef/knife/cfn_events.rb
47
+ - lib/chef/knife/cfn_validate.rb
48
+ - lib/knife-cnf/version.rb
49
+ homepage: https://github.com/neillturner/knife-cnf
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.7.2
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: CloudFormation Support for Knife
73
+ test_files: []