knife-azure 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/Gemfile +22 -0
  2. data/Guardfile +8 -0
  3. data/LICENSE +201 -0
  4. data/Rakefile +51 -0
  5. data/knife-azure.gemspec +106 -0
  6. data/lib/azure/connection.rb +75 -0
  7. data/lib/azure/deploy.rb +114 -0
  8. data/lib/azure/disk.rb +62 -0
  9. data/lib/azure/host.rb +90 -0
  10. data/lib/azure/image.rb +58 -0
  11. data/lib/azure/rest.rb +97 -0
  12. data/lib/azure/role.rb +182 -0
  13. data/lib/azure/utility.rb +29 -0
  14. data/lib/chef/knife/azure_base.rb +102 -0
  15. data/lib/chef/knife/azure_image_list.rb +58 -0
  16. data/lib/chef/knife/azure_server_create.rb +283 -0
  17. data/lib/chef/knife/azure_server_delete.rb +103 -0
  18. data/lib/chef/knife/azure_server_describe.rb +85 -0
  19. data/lib/chef/knife/azure_server_list.rb +70 -0
  20. data/lib/knife-azure/version.rb +7 -0
  21. data/readme.rdoc +210 -0
  22. data/spec/functional/deploys_test.rb +39 -0
  23. data/spec/functional/host_test.rb +22 -0
  24. data/spec/functional/images_list_test.rb +44 -0
  25. data/spec/functional/role_test.rb +16 -0
  26. data/spec/integration/role_lifecycle_test.rb +60 -0
  27. data/spec/spec_helper.rb +41 -0
  28. data/spec/unit/assets/create_deployment.xml +37 -0
  29. data/spec/unit/assets/create_deployment_in_progress.xml +1 -0
  30. data/spec/unit/assets/create_host.xml +7 -0
  31. data/spec/unit/assets/create_role.xml +54 -0
  32. data/spec/unit/assets/list_deployments_for_service000.xml +126 -0
  33. data/spec/unit/assets/list_deployments_for_service001.xml +166 -0
  34. data/spec/unit/assets/list_deployments_for_service002.xml +1 -0
  35. data/spec/unit/assets/list_deployments_for_service003.xml +1 -0
  36. data/spec/unit/assets/list_disks.xml +1 -0
  37. data/spec/unit/assets/list_hosts.xml +1 -0
  38. data/spec/unit/assets/list_images.xml +1 -0
  39. data/spec/unit/assets/post_success.xml +6 -0
  40. data/spec/unit/deploys_list_spec.rb +55 -0
  41. data/spec/unit/disks_spec.rb +44 -0
  42. data/spec/unit/hosts_spec.rb +55 -0
  43. data/spec/unit/images_spec.rb +35 -0
  44. data/spec/unit/query_azure_mock.rb +69 -0
  45. data/spec/unit/roles_create_spec.rb +82 -0
  46. data/spec/unit/roles_list_spec.rb +32 -0
  47. metadata +240 -0
data/lib/azure/host.rb ADDED
@@ -0,0 +1,90 @@
1
+ #
2
+ # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
+ # Copyright:: Copyright (c) 2010-2011 Opscode, 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
+ class Azure
20
+ class Hosts
21
+ def initialize(connection)
22
+ @connection=connection
23
+ end
24
+ def all
25
+ hosted_services = Array.new
26
+ responseXML = @connection.query_azure('hostedservices')
27
+ servicesXML = responseXML.css('HostedServices HostedService')
28
+ servicesXML.each do |serviceXML|
29
+ host = Host.new(@connection)
30
+ hosted_services << host.parse(serviceXML)
31
+ end
32
+ hosted_services
33
+ end
34
+ def exists(name)
35
+ hostExists = false
36
+ self.all.each do |host|
37
+ next unless host.name == name
38
+ hostExists = true
39
+ end
40
+ hostExists
41
+ end
42
+ def create(params)
43
+ host = Host.new(@connection)
44
+ host.create(params)
45
+ end
46
+ def delete(name)
47
+ if self.exists name
48
+ servicecall = "hostedservices/" + name
49
+ @connection.query_azure(servicecall, "delete")
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ class Azure
56
+ class Host
57
+ include AzureUtility
58
+ attr_accessor :connection, :name, :url, :label
59
+ attr_accessor :dateCreated, :description, :location
60
+ attr_accessor :dateModified, :status
61
+ def initialize(connection)
62
+ @connection = connection
63
+ end
64
+ def parse(serviceXML)
65
+ @name = xml_content(serviceXML, 'ServiceName')
66
+ @url = xml_content(serviceXML, 'Url')
67
+ @label = xml_content(serviceXML, 'HostedServiceProperties Label')
68
+ @dateCreated = xml_content(serviceXML, 'HostedServiceProperties DateCreated')
69
+ @description = xml_content(serviceXML, 'HostedServiceProperties Description')
70
+ @location = xml_content(serviceXML, 'HostedServiceProperties Location')
71
+ @dateModified = xml_content(serviceXML, 'HostedServiceProperties DateLastModified')
72
+ @status = xml_content(serviceXML, 'HostedServiceProperties Status')
73
+ self
74
+ end
75
+ def create(params)
76
+ builder = Nokogiri::XML::Builder.new do |xml|
77
+ xml.CreateHostedService('xmlns'=>'http://schemas.microsoft.com/windowsazure') {
78
+ xml.ServiceName params[:hosted_service_name]
79
+ xml.Label Base64.encode64(params[:hosted_service_name])
80
+ xml.Description params['hosted_service_description'] || 'Explicitly created hosted service'
81
+ xml.Location params['hosted_service_location'] || 'Windows Azure Preview'
82
+ }
83
+ end
84
+ @connection.query_azure("hostedservices", "post", builder.to_xml)
85
+ end
86
+ def details
87
+ response = @connection.query_azure('hostedservices/' + @name + '?embed-detail=true')
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,58 @@
1
+ #
2
+ # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
+ # Copyright:: Copyright (c) 2010-2011 Opscode, 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
+ class Azure
20
+ class Images
21
+ def initialize(connection)
22
+ @connection=connection
23
+ end
24
+ def all
25
+ images = Array.new
26
+ response = @connection.query_azure('images')
27
+ osimages = response.css('OSImage')
28
+ osimages.each do |image|
29
+ item = Image.new(image)
30
+ images << item
31
+ end
32
+ images
33
+ end
34
+ def exists(name)
35
+ imageExists = false
36
+ self.all.each do |host|
37
+ next unless host.name == name
38
+ imageExists = true
39
+ end
40
+ imageExists
41
+ end
42
+ end
43
+ end
44
+
45
+ class Azure
46
+ class Image
47
+ attr_accessor :category, :label
48
+ attr_accessor :name, :os, :eula, :description
49
+ def initialize(image)
50
+ @category = image.at_css('Category').content
51
+ @label = image.at_css('Label').content
52
+ @name = image.at_css('Name').content
53
+ @os = image.at_css('OS').content
54
+ @eula = image.at_css('Eula').content
55
+ @description = image.at_css('Description').content
56
+ end
57
+ end
58
+ end
data/lib/azure/rest.rb ADDED
@@ -0,0 +1,97 @@
1
+ #
2
+ # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
+ # Copyright:: Copyright (c) 2010-2011 Opscode, 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
+ require "net/https"
19
+ require "uri"
20
+ require "nokogiri"
21
+
22
+ module AzureAPI
23
+
24
+ class Rest
25
+ def initialize(params)
26
+ @subscription_id = params[:azure_subscription_id]
27
+ @pem_file = File.read find_pem(params[:azure_pem_file])
28
+ @host_name = params[:azure_host_name]
29
+ end
30
+ def find_pem(name)
31
+ config_dir = Chef::Knife.chef_config_dir
32
+ if File.exist? name
33
+ pem_file = name
34
+ elsif config_dir && File.exist?(File.join(config_dir, name))
35
+ pem_file = File.join(config_dir, name)
36
+ elsif File.exist?(File.join(ENV['HOME'], '.chef', name))
37
+ pem_file = File.join(ENV['HOME'], '.chef', name)
38
+ else
39
+ raise 'Unable to find certificate pem file - ' + name
40
+ end
41
+ pem_file
42
+ end
43
+ def query_azure(service_name, verb = 'get', body = '')
44
+ request_url = "https://#{@host_name}/#{@subscription_id}/services/#{service_name}"
45
+ print '.'
46
+ uri = URI.parse(request_url)
47
+ http = http_setup(uri)
48
+ request = request_setup(uri, verb, body)
49
+ response = http.request(request)
50
+ @last_request_id = response['x-ms-request-id']
51
+ response
52
+ end
53
+ def query_for_completion()
54
+ request_url = "https://#{@host_name}/#{@subscription_id}/operations/#{@last_request_id}"
55
+ uri = URI.parse(request_url)
56
+ http = http_setup(uri)
57
+ request = request_setup(uri, 'get', '')
58
+ response = http.request(request)
59
+ end
60
+ def http_setup(uri)
61
+ http = Net::HTTP.new(uri.host, uri.port)
62
+ store = OpenSSL::X509::Store.new
63
+ store.add_cert(OpenSSL::X509::Certificate.new(File.read(find_pem("cacert.pem"))))
64
+ http.cert_store = store
65
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
66
+ http.use_ssl = true
67
+ http.cert = OpenSSL::X509::Certificate.new(@pem_file)
68
+ http.key = OpenSSL::PKey::RSA.new(@pem_file)
69
+ http
70
+ end
71
+ def request_setup(uri, verb, body)
72
+ if verb == 'get'
73
+ request = Net::HTTP::Get.new(uri.request_uri)
74
+ elsif verb == 'post'
75
+ request = Net::HTTP::Post.new(uri.request_uri)
76
+ elsif verb == 'delete'
77
+ request = Net::HTTP::Delete.new(uri.request_uri)
78
+ end
79
+ request["x-ms-version"] = "2012-03-01"
80
+ request["content-type"] = "application/xml"
81
+ request["accept"] = "application/xml"
82
+ request["accept-charset"] = "utf-8"
83
+ request.body = body
84
+ request
85
+ end
86
+ def showResponse(response)
87
+ puts "=== response body ==="
88
+ puts response.body
89
+ puts "=== response.code ==="
90
+ puts response.code
91
+ puts "=== response.inspect ==="
92
+ puts response.inspect
93
+ puts "=== all of the headers ==="
94
+ puts response.each_header { |h, j| puts h.inspect + ' : ' + j.inspect}
95
+ end
96
+ end
97
+ end
data/lib/azure/role.rb ADDED
@@ -0,0 +1,182 @@
1
+ #
2
+ # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
+ # Copyright:: Copyright (c) 2010-2011 Opscode, 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
+ class Azure
20
+ class Roles
21
+ attr_accessor :connection, :roles
22
+ def initialize(connection)
23
+ @connection = connection
24
+ @roles = nil
25
+ end
26
+ def all
27
+ @roles = Array.new
28
+ @connection.deploys.all.each do |deploy|
29
+ deploy.roles.each do |role|
30
+ @roles << role
31
+ end
32
+ end
33
+ @roles
34
+ end
35
+ def find(name)
36
+ if @roles == nil
37
+ all
38
+ end
39
+ @roles.each do |role|
40
+ if(role.name == name)
41
+ return role
42
+ end
43
+ end
44
+ nil
45
+ end
46
+ def alone_on_host(name)
47
+ found_role = find(name)
48
+ @roles.each do |role|
49
+ if (role.name != found_role.name &&
50
+ role.deployname == found_role.deployname &&
51
+ role.hostedservicename == found_role.hostedservicename)
52
+ return false;
53
+ end
54
+ end
55
+ true
56
+ end
57
+ def exists(name)
58
+ find(name) != nil
59
+ end
60
+ def delete(name)
61
+ role = find(name)
62
+ if role != nil
63
+ if alone_on_host(name)
64
+ servicecall = "hostedservices/#{role.hostedservicename}/deployments" +
65
+ "/#{role.deployname}"
66
+ else
67
+ servicecall = "hostedservices/#{role.hostedservicename}/deployments" +
68
+ "/#{role.deployname}/roles/#{role.name}"
69
+ end
70
+ @connection.query_azure(servicecall, "delete")
71
+ end
72
+ #@connection.disks.clear_unattached
73
+ end
74
+ end
75
+ class Role
76
+ include AzureUtility
77
+ attr_accessor :connection, :name, :status, :size, :ipaddress
78
+ attr_accessor :sshport, :sshipaddress, :hostedservicename, :deployname
79
+ attr_accessor :hostname, :tcpports, :udpports
80
+ def initialize(connection)
81
+ @connection = connection
82
+ end
83
+ def parse(roleXML, hostedservicename, deployname)
84
+ @name = xml_content(roleXML, 'RoleName')
85
+ @status = xml_content(roleXML, 'InstanceStatus')
86
+ @size = xml_content(roleXML, 'InstanceSize')
87
+ @ipaddress = xml_content(roleXML, 'IpAddress')
88
+ @hostname = xml_content(roleXML, 'HostName')
89
+ @hostedservicename = hostedservicename
90
+ @deployname = deployname
91
+ @tcpports = Array.new
92
+ @udpports = Array.new
93
+ endpoints = roleXML.css('InstanceEndpoint')
94
+ endpoints.each do |endpoint|
95
+ if xml_content(endpoint, 'Name').downcase == 'ssh'
96
+ @sshport = xml_content(endpoint, 'PublicPort')
97
+ @sshipaddress = xml_content(endpoint, 'Vip')
98
+ else
99
+ hash = Hash.new
100
+ hash['Name'] = xml_content(endpoint, 'Name')
101
+ hash['Vip'] = xml_content(endpoint, 'Vip')
102
+ hash['PublicPort'] = xml_content(endpoint, 'PublicPort')
103
+ hash['LocalPort'] = xml_content(endpoint, 'LocalPort')
104
+ if xml_content(endpoint, 'Protocol') == 'tcp'
105
+ @tcpports << hash
106
+ else # == 'udp'
107
+ @udpports << hash
108
+ end
109
+ end
110
+ end
111
+ end
112
+ def setup(params)
113
+ builder = Nokogiri::XML::Builder.new do |xml|
114
+ xml.PersistentVMRole(
115
+ 'xmlns'=>'http://schemas.microsoft.com/windowsazure',
116
+ 'xmlns:i'=>'http://www.w3.org/2001/XMLSchema-instance'
117
+ ) {
118
+ xml.RoleName {xml.text params[:role_name]}
119
+ xml.OsVersion('i:nil' => 'true')
120
+ xml.RoleType 'PersistentVMRole'
121
+ xml.ConfigurationSets {
122
+ xml.ConfigurationSet('i:type' => 'LinuxProvisioningConfigurationSet') {
123
+ xml.ConfigurationSetType 'LinuxProvisioningConfiguration'
124
+ xml.HostName params[:host_name]
125
+ xml.UserName params[:ssh_user]
126
+ xml.UserPassword params[:ssh_password]
127
+ xml.DisableSshPasswordAuthentication 'false'
128
+ }
129
+ xml.ConfigurationSet('i:type' => 'NetworkConfigurationSet') {
130
+ xml.ConfigurationSetType 'NetworkConfiguration'
131
+ xml.InputEndpoints {
132
+ xml.InputEndpoint {
133
+ xml.LocalPort '22'
134
+ xml.Name 'SSH'
135
+ xml.Protocol 'TCP'
136
+ }
137
+ if params[:tcp_endpoints]
138
+ params[:tcp_endpoints].split(',').each do |endpoint|
139
+ ports = endpoint.split(':')
140
+ xml.InputEndpoint {
141
+ xml.LocalPort ports[0]
142
+ xml.Name 'tcpport_' + ports[0] + '_' + params[:host_name]
143
+ if ports.length > 1
144
+ xml.Port ports[1]
145
+ end
146
+ xml.Protocol 'TCP'
147
+ }
148
+ end
149
+ end
150
+ if params[:udp_endpoints]
151
+ params[:udp_endpoints].split(',').each do |endpoint|
152
+ ports = endpoint.split(':')
153
+ xml.InputEndpoint {
154
+ xml.LocalPort ports[0]
155
+ xml.Name 'udpport_' + ports[0] + '_' + params[:host_name]
156
+ if ports.length > 1
157
+ xml.Port ports[1]
158
+ end
159
+ xml.Protocol 'UDP'
160
+ }
161
+ end
162
+ end
163
+ }
164
+ }
165
+ }
166
+ xml.Label Base64.encode64(params[:role_name]).strip
167
+ xml.OSVirtualHardDisk {
168
+ xml.MediaLink 'http://' + params[:media_location_prefix] + 'imagestore.blob.core.azure-preview.com/os-disks/' + (params[:os_disk_name] || Time.now.strftime('disk_%Y_%m_%d_%H_%M'))
169
+ xml.SourceImageName params[:source_image]
170
+ }
171
+ xml.RoleSize params[:role_size]
172
+ }
173
+ end
174
+ builder.doc
175
+ end
176
+ def create(params, roleXML)
177
+ servicecall = "hostedservices/#{params[:hosted_service_name]}/deployments" +
178
+ "/#{params['deploy_name']}/roles"
179
+ @connection.query_azure(servicecall, "post", roleXML.to_xml)
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,29 @@
1
+ #
2
+ # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
+ # Copyright:: Copyright (c) 2010-2011 Opscode, 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
+ module AzureUtility
20
+ def xml_content(xml, key, default='')
21
+ content = default
22
+ node = xml.at_css(key)
23
+ if node
24
+ content = node.content
25
+ end
26
+ content
27
+ end
28
+ end
29
+
@@ -0,0 +1,102 @@
1
+
2
+ # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
4
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef/knife'
21
+ require File.expand_path('../../../azure/connection', __FILE__)
22
+
23
+ class Chef
24
+ class Knife
25
+ module AzureBase
26
+
27
+ # :nodoc:
28
+ # Would prefer to do this in a rational way, but can't be done b/c of
29
+ # Mixlib::CLI's design :(
30
+ def self.included(includer)
31
+ includer.class_eval do
32
+
33
+ deps do
34
+ require 'readline'
35
+ require 'chef/json_compat'
36
+ end
37
+
38
+ option :azure_subscription_id,
39
+ :short => "-S ID",
40
+ :long => "--azure-subscription-id ID",
41
+ :description => "Your Azure subscription ID",
42
+ :proc => Proc.new { |key| Chef::Config[:knife][:azure_subscription_id] = key }
43
+
44
+ option :azure_pem_file,
45
+ :short => "-p FILENAME",
46
+ :long => "--azure-pem-filename FILENAME",
47
+ :description => "Your Azure PEM file name",
48
+ :proc => Proc.new { |key| Chef::Config[:knife][:azure_pem_file] = key }
49
+
50
+ option :azure_host_name,
51
+ :short => "-H HOSTNAME",
52
+ :long => "--azure_host_name HOSTNAME",
53
+ :description => "Your Azure host name",
54
+ :proc => Proc.new { |key| Chef::Config[:knife][:azure_host_name] = key }
55
+
56
+ end
57
+ end
58
+
59
+ def connection
60
+ @connection ||= begin
61
+ connection = Azure::Connection.new(
62
+ :azure_subscription_id =>
63
+ config[:azure_subscription_id] || Chef::Config[:knife][:azure_subscription_id],
64
+ :azure_pem_file =>
65
+ config[:azure_pem_file] || Chef::Config[:knife][:azure_pem_file],
66
+ :azure_host_name =>
67
+ config[:azure_host_name] || Chef::Config[:knife][:azure_host_name]
68
+ )
69
+ end
70
+ end
71
+
72
+ def locate_config_value(key)
73
+ key = key.to_sym
74
+ config[key] || Chef::Config[:knife][key]
75
+ end
76
+
77
+ def msg_pair(label, value, color=:cyan)
78
+ if value && !value.to_s.empty?
79
+ puts "#{ui.color(label, color)}: #{value}"
80
+ end
81
+ end
82
+
83
+ def validate!(keys=[:azure_subscription_id, :azure_pem_file, :azure_host_name])
84
+ errors = []
85
+
86
+ keys.each do |k|
87
+ pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
88
+ if Chef::Config[:knife][k].nil?
89
+ errors << "You did not provide a valid '#{pretty_key}' value."
90
+ end
91
+ end
92
+
93
+ if errors.each{|e| ui.error(e)}.any?
94
+ exit 1
95
+ end
96
+ end
97
+
98
+ end
99
+ end
100
+ end
101
+
102
+
@@ -0,0 +1,58 @@
1
+ #
2
+ # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
4
+ # Author:: Adam Jacob (<adam@opscode.com>)
5
+ # Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require File.expand_path('../azure_base', __FILE__)
22
+
23
+ class Chef
24
+ class Knife
25
+ class AzureImageList < Knife
26
+
27
+ include Knife::AzureBase
28
+
29
+ banner "knife azure image list (options)"
30
+
31
+ def run
32
+ $stdout.sync = true
33
+
34
+ validate!
35
+
36
+ image_list = [
37
+ ui.color('Name', :bold),
38
+ #ui.color('Category', :bold),
39
+ #ui.color('Label', :bold),
40
+ #ui.color('OS', :bold),
41
+ #ui.color('Eula', :bold),
42
+ ]
43
+ items = connection.images.all
44
+ items.each do |image|
45
+ if image.os == 'Linux'
46
+ image_list << image.name.to_s
47
+ #image_list << image.category.to_s
48
+ #image_list << image.label.to_s
49
+ #image_list << image.os.to_s
50
+ #image_list << image.eula.to_s
51
+ end
52
+ end
53
+ puts ''
54
+ puts ui.list(image_list, :columns_across, 1)
55
+ end
56
+ end
57
+ end
58
+ end