knife-azure 1.0.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.
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