knife-cfn 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []