knife-azure 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/{readme.rdoc → README.rdoc} +68 -26
  2. data/lib/azure/connection.rb +3 -1
  3. data/lib/azure/deploy.rb +3 -0
  4. data/lib/azure/host.rb +2 -2
  5. data/lib/azure/image.rb +2 -2
  6. data/lib/azure/rest.rb +8 -3
  7. data/lib/azure/role.rb +1 -1
  8. data/lib/azure/storageaccount.rb +87 -0
  9. data/lib/chef/knife/azure_base.rb +19 -17
  10. data/lib/chef/knife/azure_image_list.rb +13 -10
  11. data/lib/chef/knife/azure_server_create.rb +45 -13
  12. data/lib/knife-azure/version.rb +1 -1
  13. metadata +208 -178
  14. data/Gemfile +0 -22
  15. data/Guardfile +0 -8
  16. data/Rakefile +0 -51
  17. data/knife-azure.gemspec +0 -106
  18. data/spec/functional/deploys_test.rb +0 -39
  19. data/spec/functional/host_test.rb +0 -22
  20. data/spec/functional/images_list_test.rb +0 -44
  21. data/spec/functional/role_test.rb +0 -16
  22. data/spec/integration/role_lifecycle_test.rb +0 -60
  23. data/spec/spec_helper.rb +0 -41
  24. data/spec/unit/assets/create_deployment.xml +0 -37
  25. data/spec/unit/assets/create_deployment_in_progress.xml +0 -1
  26. data/spec/unit/assets/create_host.xml +0 -7
  27. data/spec/unit/assets/create_role.xml +0 -54
  28. data/spec/unit/assets/list_deployments_for_service000.xml +0 -126
  29. data/spec/unit/assets/list_deployments_for_service001.xml +0 -166
  30. data/spec/unit/assets/list_deployments_for_service002.xml +0 -1
  31. data/spec/unit/assets/list_deployments_for_service003.xml +0 -1
  32. data/spec/unit/assets/list_disks.xml +0 -1
  33. data/spec/unit/assets/list_hosts.xml +0 -1
  34. data/spec/unit/assets/list_images.xml +0 -1
  35. data/spec/unit/assets/post_success.xml +0 -6
  36. data/spec/unit/deploys_list_spec.rb +0 -55
  37. data/spec/unit/disks_spec.rb +0 -44
  38. data/spec/unit/hosts_spec.rb +0 -55
  39. data/spec/unit/images_spec.rb +0 -35
  40. data/spec/unit/query_azure_mock.rb +0 -69
  41. data/spec/unit/roles_create_spec.rb +0 -82
  42. data/spec/unit/roles_list_spec.rb +0 -32
@@ -2,18 +2,42 @@
2
2
 
3
3
  Description: This plugin supports listing, creating, and deleting Azure instances bootstrapped with chef client.
4
4
 
5
+ ==Generating the Management Certifcate (PEM)
6
+ The Management Certificate is required for secure communication with the Windows Azure Platform via the REST APIs.
7
+ Follow these steps to generate the certificate
8
+
9
+ 1. Download the settings file from http://go.microsoft.com/fwlink/?LinkId=254432
10
+ 2. Extract the data from the ManagementCertificate field into a separate file named - cert.pfx
11
+ 3. Decode the certificate file :
12
+
13
+ base64 -d cert.pfx > cert_decoded.pfx
14
+
15
+ 4. Convert the decoded PFX file to a PEM file
16
+
17
+ openssl pkcs12 -in cert_decoded.pfx -out managementCertificate.pem -nodes
18
+
19
+ It is possible to generate your own certificates and upload them. More Detailed Documentation about the Management Certificates is available : https://www.windowsazure.com/en-us/manage/linux/common-tasks/manage-certificates/
20
+
5
21
  ==Installation:
22
+ The plugin relies on SSL for communication and hence requires the SSL certifcate is setup. The Environment variable SSL_CERT_FILE should point to the full path of the SSL Certificate file.(optional)
23
+
6
24
  Be sure you are running the latest version Chef. Versions earlier than 0.10.0 don’t support plugins:
7
25
 
8
26
  gem install chef
9
- This plugin is distributed as a Ruby Gem. To install it, run:
10
27
 
11
- gem install knife-azure
28
+ This plugin is distributed as a Ruby Gem. To install it, with missing dependencies run:
29
+
30
+ bundle install
31
+
32
+ If you make any changes to the knife plugin code and want to re-install the plugin:
33
+
34
+ rake install
35
+
12
36
  Depending on your system’s configuration, you may need to run this command with root privileges.
13
37
 
14
38
  ==Requirements:
15
39
 
16
- Due to a bug in mixlib-authentication, Chef node names must be 91 characters or shorter. Chef uses the host name by default, however the host names provided by Azure are very long.
40
+ Due to a bug in mixlib-authentication, Chef node names must be 91 characters or shorter. Chef uses the host name by default, however the host names provided by Azure are very long.
17
41
 
18
42
  You must use the '-N NODE_NAME' flag with 'knife azure' to specify a node name to be used by Chef that is less than 91 characters.
19
43
 
@@ -28,15 +52,22 @@ option :azure_subscription_id,
28
52
  :long => "--azure-subscription-id ID",
29
53
  :description => "Your Azure subscription ID",
30
54
 
31
- option :azure_pem_file,
55
+ option :azure_mgmt_cert,
32
56
  :short => "-p FILENAME",
33
- :long => "--azure-pem-filename FILENAME",
34
- :description => "Your Azure PEM file name",
57
+ :long => "--azure-mgmt-cert FILENAME",
58
+ :description => "Your Azure Management Certificate File",
35
59
 
36
60
  option :azure_host_name,
37
61
  :short => "-H HOSTNAME",
38
- :long => "--azure_host_name HOSTNAME",
62
+ :long => "--azure-host-name HOSTNAME",
39
63
  :description => "Your Azure host name",
64
+ :default => "management.core.windows.net"
65
+
66
+ option :verify_ssl_cert,
67
+ :long => "--verify-ssl-cert",
68
+ :description => "Verify SSL Certificates for communication over HTTPS",
69
+ :boolean => true,
70
+ :default => false
40
71
 
41
72
  Options used with the Create subcommand:
42
73
  option :chef_node_name,
@@ -72,7 +103,7 @@ option :distro,
72
103
  :short => "-d DISTRO",
73
104
  :long => "--distro DISTRO",
74
105
  :description => "Bootstrap a distro using a template",
75
- :default => "chef-full"
106
+ :default => "ubuntu10.04-gems"
76
107
 
77
108
  option :template_file,
78
109
  :long => "--template-file TEMPLATE",
@@ -96,20 +127,29 @@ option :hosted_service_name,
96
127
  :long => "--hosted-service-name NAME",
97
128
  :description => "specifies the name for the hosted service"
98
129
 
130
+ option :hosted_service_description,
131
+ :short => "-D DESCRIPTION",
132
+ :long => "--hosted_service_description DESCRIPTION",
133
+ :description => "Description for the hosted service"
134
+
99
135
  option :role_name,
100
136
  :short => "-R name",
101
137
  :long => "-- role-name NAME",
102
138
  :description => "specifies the name for the virtual machine"
103
139
 
104
140
  option :host_name,
105
- :short => "-H NAME",
106
141
  :long => "--host-name NAME",
107
142
  :description => "specifies the host name for the virtual machine"
108
143
 
109
- option :media_location_prefix,
110
- :short => "-m PREFIX",
111
- :long => "--media-location-prefix PREFIX",
112
- :description => "user account name (used for constructing os disk media link)"
144
+ option :storage_account,
145
+ :short => "-a NAME",
146
+ :long => "--storage-account NAME",
147
+ :description => "specifies the name for the hosted service"
148
+
149
+ option :service_location,
150
+ :short => "-m LOCATION",
151
+ :long => "--service-location LOCATION",
152
+ :description => "specify the Geographic location for the virtual machine and services. eg. West US, East US, North Europe East Asia etc"
113
153
 
114
154
  option :os_disk_name,
115
155
  :short => "-o DISKNAME",
@@ -138,11 +178,13 @@ option :udp_endpoints,
138
178
 
139
179
 
140
180
  ====Here are some lines with example values in a knife.rb file:
141
- knife[:azure_subscription_id] = "YOUR-GUID"
181
+ knife[:azure_subscription_id] = "155a9851-88a8-49b4-98e4-58055f08f412"
142
182
 
143
- knife[:azure_pem_file] = "YOUR-CERT.pem"
183
+ knife[:azure_mgmt_cert] = "ManagementCertificate.pem"
144
184
 
145
- knife[:azure_host_name] = "azure-api-endpoint"
185
+ knife[:azure_host_name] = "management.core.windows.net"
186
+
187
+ knife[:service_location] = 'West US'
146
188
 
147
189
  knife[:hosted_service_name]='service001'
148
190
 
@@ -150,11 +192,11 @@ knife[:role_name]='role105'
150
192
 
151
193
  knife[:host_name]='host105'
152
194
 
153
- knife[:ssh_user]='yoursshuser'
195
+ knife[:ssh_user]='jetstream'
154
196
 
155
- knife[:ssh_password]='yoursshpw'
197
+ knife[:ssh_password]='jetstream1!'
156
198
 
157
- knife[:media_location_prefix]='auxpreview104'
199
+ knife[:storage_account]='auxpreview104'
158
200
 
159
201
  knife[:os_disk_name]='disk107'
160
202
 
@@ -184,13 +226,13 @@ knife[:role_size]='Small'
184
226
  This plugin provides the following Knife subcommands. Specific command options can be found by invoking the subcommand with a --help flag
185
227
 
186
228
  ===knife azure server create
187
- Provisions a new server in Azure and then perform a Chef bootstrap (using the SSH protocol). The goal of the bootstrap is to get Chef installed on the target system so it can run Chef Client with a Chef Server. The main assumption is a baseline OS installation exists (provided by the provisioning). It is primarily intended for Chef Client systems that talk to a Chef server. By default the server is bootstrapped using the ubuntu10.04-gems template. This can be overridden using the -d or --template-file command options.
229
+ Provisions a new server in Azure and then perform a Chef bootstrap (using the SSH protocol). The goal of the bootstrap is to get Chef installed on the target system so it can run Chef Client with a Chef Server. The main assumption is a baseline OS installation exists (provided by the provisioning). It is primarily intended for Chef Client systems that talk to a Chef server. By default the server is bootstrapped using the ubuntu10.04-gems template. This can be overridden using the -d or --template-file command options. (*** Note that at the current time only CentOS is supported for bootstrapping and required the :distro to be set to centos5-gems. There is a fix in the works from Christpher Brown in mixlib-authentication I believe that fixes an RSA encryption too long error that occurs for some hostnames during bootstrap. ***)
188
230
 
189
231
  ===knife azure server delete [role_name_to_delete]
190
- Deletes an existing server(role) in the currently configured AWS account. PLEASE NOTE - By default, this does not delete the associated node and client objects from the Chef server. To do so, add the --purge flag.
232
+ Deletes an existing server(role) in the currently configured Azure account. PLEASE NOTE - By default, this does not delete the associated node and client objects from the Chef server. To do so, add the --purge flag.
191
233
 
192
234
  ===knife azure server list
193
- Outputs a list of all servers in the currently configured AWS account. PLEASE NOTE - this shows all instances associated with the account, some of which may not be currently managed by the Chef server.
235
+ Outputs a list of all servers in the currently configured Azure account. PLEASE NOTE - this shows all instances associated with the account, some of which may not be currently managed by the Chef server.
194
236
 
195
237
  ===knife azure server describe [role_name_to_describe]
196
238
  Outputs detail about a specific role, including all the ports that it has open
@@ -198,13 +240,13 @@ Outputs detail about a specific role, including all the ports that it has open
198
240
  ===knife azure image list
199
241
  Outputs a list of all linux images that are available to use for provisioning. You should choose one of these to use for the :source_image parameter to the server create command.
200
242
 
243
+
244
+ ===knife azure server describe
245
+ Output additional information about the provisioned Virtual Machine
246
+
201
247
  == Understanding Azure
202
248
  Azure implements the following hierarchy - subscription=>hosted service=>deployment=>role (and the guest operating system has a hostname as well, which uses the role as its container)
203
249
 
204
250
  These are generally a one to many relationship from top to bottom, however there are two anamolies relating to the deployment
205
251
  1) a hosted service can have more than one deployment, but that seems to be an artifact of the PAAS origins of Azure. PAAS allows there to be one staging and one production deployment per hosted service. It is my understanding (and how the code works) that there should be only one deployment per hosted service. Some initial internal code I examined used the technique of looking at the "production" deployment slot to iterate for existing roles. If a create request occurs and a deployment does not exist, it is created and given the same name as the hosted service and the deployment slot is marked as "production".
206
252
  2) Azure enforces that a deployment must include the initial role when it is created. It also will not allow you to delete a role if it is the last remaining role in a deployment; in that case you are required to delete the deployment.
207
-
208
- ==Tests:
209
-
210
- The tests require a subscription id to be places in spec/spec_helper.rb and the associated private key to be placed in 'AzureLinuxCert.pem' in the top level directory. Then run 'rake spec'
@@ -18,6 +18,7 @@
18
18
  require File.expand_path('../utility', __FILE__)
19
19
  require File.expand_path('../rest', __FILE__)
20
20
  require File.expand_path('../host', __FILE__)
21
+ require File.expand_path('../storageaccount', __FILE__)
21
22
  require File.expand_path('../deploy', __FILE__)
22
23
  require File.expand_path('../role', __FILE__)
23
24
  require File.expand_path('../disk', __FILE__)
@@ -26,10 +27,11 @@ require File.expand_path('../image', __FILE__)
26
27
  class Azure
27
28
  class Connection
28
29
  include AzureAPI
29
- attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks
30
+ attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks, :storageaccounts
30
31
  def initialize(params={})
31
32
  @rest = Rest.new(params)
32
33
  @hosts = Hosts.new(self)
34
+ @storageaccounts = StorageAccounts.new(self)
33
35
  @images = Images.new(self)
34
36
  @deploys = Deploys.new(self)
35
37
  @roles = Roles.new(self)
data/lib/azure/deploy.rb CHANGED
@@ -45,6 +45,9 @@ class Azure
45
45
  unless @connection.hosts.exists(params[:hosted_service_name])
46
46
  @connection.hosts.create(params)
47
47
  end
48
+ unless @connection.storageaccounts.exists(params[:storage_account])
49
+ @connection.storageaccounts.create(params)
50
+ end
48
51
  params['deploy_name'] = find(params[:hosted_service_name])
49
52
  if params['deploy_name'] != nil
50
53
  role = Role.new(@connection)
data/lib/azure/host.rb CHANGED
@@ -77,8 +77,8 @@ class Azure
77
77
  xml.CreateHostedService('xmlns'=>'http://schemas.microsoft.com/windowsazure') {
78
78
  xml.ServiceName params[:hosted_service_name]
79
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'
80
+ xml.Description params[:hosted_service_description] || 'Explicitly created hosted service'
81
+ xml.Location params[:service_location] || 'West US'
82
82
  }
83
83
  end
84
84
  @connection.query_azure("hostedservices", "post", builder.to_xml)
data/lib/azure/image.rb CHANGED
@@ -51,8 +51,8 @@ class Azure
51
51
  @label = image.at_css('Label').content
52
52
  @name = image.at_css('Name').content
53
53
  @os = image.at_css('OS').content
54
- @eula = image.at_css('Eula').content
55
- @description = image.at_css('Description').content
54
+ @eula = image.at_css('Eula').content if image.at_css('Eula')
55
+ @description = image.at_css('Description').content if image.at_css('Description')
56
56
  end
57
57
  end
58
58
  end
data/lib/azure/rest.rb CHANGED
@@ -24,8 +24,9 @@ module AzureAPI
24
24
  class Rest
25
25
  def initialize(params)
26
26
  @subscription_id = params[:azure_subscription_id]
27
- @pem_file = File.read find_pem(params[:azure_pem_file])
27
+ @pem_file = File.read find_pem(params[:azure_mgmt_cert])
28
28
  @host_name = params[:azure_host_name]
29
+ @verify_ssl = params[:verify_ssl_cert]
29
30
  end
30
31
  def find_pem(name)
31
32
  config_dir = Chef::Knife.chef_config_dir
@@ -60,9 +61,13 @@ module AzureAPI
60
61
  def http_setup(uri)
61
62
  http = Net::HTTP.new(uri.host, uri.port)
62
63
  store = OpenSSL::X509::Store.new
63
- store.add_cert(OpenSSL::X509::Certificate.new(File.read(find_pem("cacert.pem"))))
64
+ store.set_default_paths
64
65
  http.cert_store = store
65
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
66
+ if @verify_ssl
67
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
68
+ else
69
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
70
+ end
66
71
  http.use_ssl = true
67
72
  http.cert = OpenSSL::X509::Certificate.new(@pem_file)
68
73
  http.key = OpenSSL::PKey::RSA.new(@pem_file)
data/lib/azure/role.rb CHANGED
@@ -165,7 +165,7 @@ class Azure
165
165
  }
166
166
  xml.Label Base64.encode64(params[:role_name]).strip
167
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'))
168
+ xml.MediaLink 'http://' + params[:storage_account] + '.blob.core.windows.net/vhds/' + (params[:os_disk_name] || Time.now.strftime('disk_%Y_%m_%d_%H_%M')) + '.vhd'
169
169
  xml.SourceImageName params[:source_image]
170
170
  }
171
171
  xml.RoleSize params[:role_size]
@@ -0,0 +1,87 @@
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 StorageAccounts
21
+ def initialize(connection)
22
+ @connection=connection
23
+ end
24
+ def all
25
+ storage_accounts = Array.new
26
+ responseXML = @connection.query_azure('storageservices')
27
+ servicesXML = responseXML.css('StorageServices StorageService')
28
+ servicesXML.each do |serviceXML|
29
+ storage_account = StorageAccount.new(@connection)
30
+ storage_accounts << storage_account.parse(serviceXML)
31
+ end
32
+ storage_accounts
33
+ end
34
+ def exists(name)
35
+ storageExists = false
36
+ self.all.each do |storage|
37
+ next unless storage.name == name
38
+ storageExists = true
39
+ end
40
+ storageExists
41
+ end
42
+ def create(params)
43
+ storage = StorageAccount.new(@connection)
44
+ storage.create(params)
45
+ end
46
+ def clear_unattached
47
+ self.all.each do |storage|
48
+ next unless storage.attached == false
49
+ @connection.query_azure('storageaccounts/' + storage.name, 'delete')
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ class Azure
56
+ class StorageAccount
57
+ include AzureUtility
58
+ attr_accessor :name, :location
59
+ attr_accessor :affinityGroup, :location, :georeplicationenabled
60
+ def initialize(connection)
61
+ @connection = connection
62
+ end
63
+ def parse(serviceXML)
64
+ @name = xml_content(serviceXML, 'ServiceName')
65
+ @description = xml_content(serviceXML, 'Description')
66
+ @label = xml_content(serviceXML, 'Label')
67
+ @affinitygroup = xml_content(serviceXML, 'AffinityGroup')
68
+ @location = xml_content(serviceXML, 'Location')
69
+ @georeplicationenabled = xml_content(serviceXML, 'GeoReplicationEnabled')
70
+ @extendpropertyname = xml_content(serviceXML, 'ExtendedProperties ExtendedProperty Name')
71
+ @extendpropertyvalue = xml_content(serviceXML, 'ExtendedProperties ExtendedProperty Value')
72
+ self
73
+ end
74
+ def create(params)
75
+ builder = Nokogiri::XML::Builder.new do |xml|
76
+ xml.CreateStorageServiceInput('xmlns'=>'http://schemas.microsoft.com/windowsazure') {
77
+ xml.ServiceName params[:storage_account]
78
+ xml.Label Base64.encode64(params[:storage_account])
79
+ xml.Description params[:storage_account_description] || 'Explicitly created storage service'
80
+ # Location defaults to 'West US'
81
+ xml.Location params[:service_location] || 'West US'
82
+ }
83
+ end
84
+ @connection.query_azure("storageservices", "post", builder.to_xml)
85
+ end
86
+ end
87
+ end
@@ -41,32 +41,36 @@ class Chef
41
41
  :description => "Your Azure subscription ID",
42
42
  :proc => Proc.new { |key| Chef::Config[:knife][:azure_subscription_id] = key }
43
43
 
44
- option :azure_pem_file,
44
+ option :azure_mgmt_cert,
45
45
  :short => "-p FILENAME",
46
- :long => "--azure-pem-filename FILENAME",
46
+ :long => "--azure-mgmt-cert FILENAME",
47
47
  :description => "Your Azure PEM file name",
48
- :proc => Proc.new { |key| Chef::Config[:knife][:azure_pem_file] = key }
48
+ :proc => Proc.new { |key| Chef::Config[:knife][:azure_mgmt_cert] = key }
49
49
 
50
50
  option :azure_host_name,
51
51
  :short => "-H HOSTNAME",
52
- :long => "--azure_host_name HOSTNAME",
52
+ :long => "--azure-host-name HOSTNAME",
53
53
  :description => "Your Azure host name",
54
- :proc => Proc.new { |key| Chef::Config[:knife][:azure_host_name] = key }
55
-
54
+ :proc => Proc.new { |key| Chef::Config[:knife][:azure_host_name] = key },
55
+ :default => "management.core.windows.net"
56
+
57
+ option :verify_ssl_cert,
58
+ :long => "--verify-ssl-cert",
59
+ :description => "Verify SSL Certificates for communication over HTTPS",
60
+ :boolean => true,
61
+ :default => false
56
62
  end
57
63
  end
58
64
 
59
65
  def connection
60
66
  @connection ||= begin
61
67
  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
+ :azure_subscription_id => locate_config_value(:azure_subscription_id),
69
+ :azure_mgmt_cert => locate_config_value(:azure_mgmt_cert),
70
+ :azure_host_name => locate_config_value(:azure_host_name),
71
+ :verify_ssl_cert => locate_config_value(:verify_ssl_cert)
68
72
  )
69
- end
73
+ end
70
74
  end
71
75
 
72
76
  def locate_config_value(key)
@@ -80,12 +84,12 @@ class Chef
80
84
  end
81
85
  end
82
86
 
83
- def validate!(keys=[:azure_subscription_id, :azure_pem_file, :azure_host_name])
87
+ def validate!(keys=[:azure_subscription_id, :azure_mgmt_cert, :azure_host_name])
84
88
  errors = []
85
89
 
86
90
  keys.each do |k|
87
91
  pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
88
- if Chef::Config[:knife][k].nil?
92
+ if locate_config_value(k).nil?
89
93
  errors << "You did not provide a valid '#{pretty_key}' value."
90
94
  end
91
95
  end
@@ -98,5 +102,3 @@ class Chef
98
102
  end
99
103
  end
100
104
  end
101
-
102
-
@@ -18,6 +18,7 @@
18
18
  # limitations under the License.
19
19
  #
20
20
 
21
+ require 'highline'
21
22
  require File.expand_path('../azure_base', __FILE__)
22
23
 
23
24
  class Chef
@@ -28,6 +29,10 @@ class Chef
28
29
 
29
30
  banner "knife azure image list (options)"
30
31
 
32
+ def h
33
+ @highline ||= HighLine.new
34
+ end
35
+
31
36
  def run
32
37
  $stdout.sync = true
33
38
 
@@ -35,23 +40,21 @@ class Chef
35
40
 
36
41
  image_list = [
37
42
  ui.color('Name', :bold),
38
- #ui.color('Category', :bold),
39
- #ui.color('Label', :bold),
40
- #ui.color('OS', :bold),
41
- #ui.color('Eula', :bold),
43
+ ui.color('Category', :bold),
44
+ ui.color('Label', :bold),
45
+ ui.color('OS', :bold),
42
46
  ]
43
47
  items = connection.images.all
44
48
  items.each do |image|
45
49
  if image.os == 'Linux'
46
50
  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
+ image_list << image.category.to_s
52
+ image_list << image.label.to_s
53
+ image_list << image.os.to_s
51
54
  end
52
55
  end
53
- puts ''
54
- puts ui.list(image_list, :columns_across, 1)
56
+ puts "\n"
57
+ puts h.list(image_list, :columns_across, 4)
55
58
  end
56
59
  end
57
60
  end