knife-azure 1.6.0.rc.0 → 1.6.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +304 -8
  3. data/lib/azure/azure_interface.rb +81 -0
  4. data/lib/azure/custom_errors.rb +35 -0
  5. data/lib/azure/helpers.rb +44 -0
  6. data/lib/azure/resource_management/ARM_base.rb +29 -0
  7. data/lib/azure/resource_management/ARM_deployment_template.rb +561 -0
  8. data/lib/azure/resource_management/ARM_interface.rb +795 -0
  9. data/lib/azure/resource_management/windows_credentials.rb +136 -0
  10. data/lib/azure/service_management/ASM_interface.rb +301 -0
  11. data/lib/azure/{ag.rb → service_management/ag.rb} +2 -2
  12. data/lib/azure/{certificate.rb → service_management/certificate.rb} +2 -2
  13. data/lib/azure/service_management/connection.rb +102 -0
  14. data/lib/azure/{deploy.rb → service_management/deploy.rb} +8 -2
  15. data/lib/azure/{disk.rb → service_management/disk.rb} +2 -2
  16. data/lib/azure/{host.rb → service_management/host.rb} +2 -2
  17. data/lib/azure/{image.rb → service_management/image.rb} +2 -2
  18. data/lib/azure/{loadbalancer.rb → service_management/loadbalancer.rb} +4 -18
  19. data/lib/azure/{rest.rb → service_management/rest.rb} +15 -10
  20. data/lib/azure/{role.rb → service_management/role.rb} +174 -6
  21. data/lib/azure/{storageaccount.rb → service_management/storageaccount.rb} +2 -2
  22. data/lib/azure/{utility.rb → service_management/utility.rb} +0 -0
  23. data/lib/azure/{vnet.rb → service_management/vnet.rb} +2 -2
  24. data/lib/chef/knife/azure_ag_create.rb +3 -6
  25. data/lib/chef/knife/azure_ag_list.rb +2 -16
  26. data/lib/chef/knife/azure_base.rb +89 -22
  27. data/lib/chef/knife/azure_image_list.rb +3 -7
  28. data/lib/chef/knife/azure_internal-lb_create.rb +2 -5
  29. data/lib/chef/knife/azure_internal-lb_list.rb +2 -16
  30. data/lib/chef/knife/azure_server_create.rb +122 -501
  31. data/lib/chef/knife/azure_server_delete.rb +15 -38
  32. data/lib/chef/knife/azure_server_list.rb +2 -27
  33. data/lib/chef/knife/azure_server_show.rb +4 -60
  34. data/lib/chef/knife/azure_vnet_create.rb +2 -7
  35. data/lib/chef/knife/azure_vnet_list.rb +2 -17
  36. data/lib/chef/knife/azurerm_base.rb +228 -0
  37. data/lib/chef/knife/azurerm_server_create.rb +393 -0
  38. data/lib/chef/knife/azurerm_server_delete.rb +121 -0
  39. data/lib/chef/knife/azurerm_server_list.rb +18 -0
  40. data/lib/chef/knife/azurerm_server_show.rb +37 -0
  41. data/lib/chef/knife/bootstrap/bootstrap_options.rb +105 -0
  42. data/lib/chef/knife/bootstrap/bootstrapper.rb +343 -0
  43. data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +116 -0
  44. data/lib/chef/knife/bootstrap_azure.rb +110 -0
  45. data/lib/chef/knife/bootstrap_azurerm.rb +116 -0
  46. data/lib/knife-azure/version.rb +1 -2
  47. metadata +132 -16
  48. data/lib/azure/connection.rb +0 -99
@@ -16,7 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- class Azure
19
+ module Azure
20
20
  class StorageAccounts
21
21
  include AzureUtility
22
22
  def initialize(connection)
@@ -82,7 +82,7 @@ class Azure
82
82
  end
83
83
  end
84
84
 
85
- class Azure
85
+ module Azure
86
86
  class StorageAccount
87
87
  include AzureUtility
88
88
  attr_accessor :name, :location
@@ -16,7 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- class Azure
19
+ module Azure
20
20
  class Vnets
21
21
  def initialize(connection)
22
22
  @connection = connection
@@ -53,7 +53,7 @@ class Azure
53
53
  end
54
54
  end
55
55
 
56
- class Azure
56
+ module Azure
57
57
  class Vnet
58
58
  attr_accessor :name, :affinity_group, :state
59
59
 
@@ -46,11 +46,8 @@ class Chef
46
46
  $stdout.sync = true
47
47
 
48
48
  Chef::Log.info('validating...')
49
- validate!([:azure_subscription_id,
50
- :azure_mgmt_cert,
51
- :azure_api_host_name,
52
- :azure_affinity_group,
53
- :azure_service_location])
49
+ validate_asm_keys!(:azure_affinity_group,
50
+ :azure_service_location)
54
51
 
55
52
  params = {
56
53
  azure_ag_name: locate_config_value(:azure_affinity_group),
@@ -58,7 +55,7 @@ class Chef
58
55
  azure_location: locate_config_value(:azure_service_location),
59
56
  }
60
57
 
61
- rsp = connection.ags.create(params)
58
+ rsp = service.create_affinity_group(params)
62
59
  print "\n"
63
60
  if rsp.at_css('Status').nil?
64
61
  if rsp.at_css('Code').nil? || rsp.at_css('Message').nil?
@@ -27,24 +27,10 @@ class Chef
27
27
 
28
28
  banner 'knife azure ag list (options)'
29
29
 
30
- def hl
31
- @highline ||= HighLine.new
32
- end
33
-
34
30
  def run
35
31
  $stdout.sync = true
36
-
37
- validate!
38
-
39
- cols = %w{Name Location Description}
40
-
41
- the_list = cols.map { |col| ui.color(col, :bold) }
42
- connection.ags.all.each do |ag|
43
- cols.each { |attr| the_list << ag.send(attr.downcase).to_s }
44
- end
45
-
46
- puts "\n"
47
- puts hl.list(the_list, :uneven_columns_across, cols.size)
32
+ validate_asm_keys!
33
+ service.list_affinity_groups
48
34
  end
49
35
  end
50
36
  end
@@ -18,7 +18,7 @@
18
18
  #
19
19
 
20
20
  require 'chef/knife'
21
- require File.expand_path('../../../azure/connection', __FILE__)
21
+ require 'azure/service_management/ASM_interface'
22
22
 
23
23
  class Chef
24
24
  class Knife
@@ -67,8 +67,8 @@ class Chef
67
67
  end
68
68
 
69
69
  def is_image_windows?
70
- images = connection.images
71
- target_image = images.all.select { |i| i.name == locate_config_value(:azure_source_image) }
70
+ images = service.list_images
71
+ target_image = images.select { |i| i.name == locate_config_value(:azure_source_image) }
72
72
  unless target_image[0].nil?
73
73
  return target_image[0].os == 'Windows'
74
74
  else
@@ -76,15 +76,18 @@ class Chef
76
76
  exit 1
77
77
  end
78
78
  end
79
- def connection
80
- @connection ||= begin
81
- connection = Azure::Connection.new(
82
- :azure_subscription_id => locate_config_value(:azure_subscription_id),
83
- :azure_mgmt_cert => locate_config_value(:azure_mgmt_cert),
84
- :azure_api_host_name => locate_config_value(:azure_api_host_name),
85
- :verify_ssl_cert => locate_config_value(:verify_ssl_cert)
86
- )
87
- end
79
+
80
+ def service
81
+ @service ||= begin
82
+ service = Azure::ServiceManagement::ASMInterface.new(
83
+ :azure_subscription_id => locate_config_value(:azure_subscription_id),
84
+ :azure_mgmt_cert => locate_config_value(:azure_mgmt_cert),
85
+ :azure_api_host_name => locate_config_value(:azure_api_host_name),
86
+ :verify_ssl_cert => locate_config_value(:verify_ssl_cert)
87
+ )
88
+ end
89
+ @service.ui = ui
90
+ @service
88
91
  end
89
92
 
90
93
  def locate_config_value(key)
@@ -116,18 +119,20 @@ class Chef
116
119
  puts "\n"
117
120
  end
118
121
 
119
- def validate!(keys=[:azure_subscription_id, :azure_mgmt_cert, :azure_api_host_name])
122
+ def pretty_key(key)
123
+ key.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
124
+ end
125
+
126
+ # validate command pre-requisites (cli options)
127
+ def validate_params!
128
+ end
129
+
130
+ # validates keys
131
+ def validate!(keys)
120
132
  errors = []
121
- if(locate_config_value(:azure_mgmt_cert) != nil)
122
- config[:azure_mgmt_cert] = File.read find_file(locate_config_value(:azure_mgmt_cert))
123
- end
124
- if(locate_config_value(:azure_publish_settings_file) != nil)
125
- parse_publish_settings_file(locate_config_value(:azure_publish_settings_file))
126
- end
127
133
  keys.each do |k|
128
- pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
129
134
  if locate_config_value(k).nil?
130
- errors << "You did not provide a valid '#{pretty_key}' value. Please set knife[:#{k}] in your knife.rb or pass as an option."
135
+ errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your knife.rb or pass as an option."
131
136
  end
132
137
  end
133
138
  if errors.each{|e| ui.error(e)}.any?
@@ -135,6 +140,26 @@ class Chef
135
140
  end
136
141
  end
137
142
 
143
+ # validate ASM mandatory keys
144
+ def validate_asm_keys!(*keys)
145
+ mandatory_keys = [:azure_subscription_id, :azure_mgmt_cert, :azure_api_host_name]
146
+ keys.concat(mandatory_keys)
147
+
148
+ if(locate_config_value(:azure_mgmt_cert) != nil)
149
+ config[:azure_mgmt_cert] = File.read find_file(locate_config_value(:azure_mgmt_cert))
150
+ end
151
+
152
+ if(locate_config_value(:azure_publish_settings_file) != nil)
153
+ parse_publish_settings_file(locate_config_value(:azure_publish_settings_file))
154
+ elsif locate_config_value(:azure_subscription_id).nil? && locate_config_value(:azure_mgmt_cert).nil? && locate_config_value(:azure_api_host_name).nil?
155
+ azureprofile_file = get_azure_profile_file_path
156
+ if File.exist?(File.expand_path(azureprofile_file))
157
+ errors = parse_azure_profile(azureprofile_file, errors)
158
+ end
159
+ end
160
+ validate!(keys)
161
+ end
162
+
138
163
  def parse_publish_settings_file(filename)
139
164
  require 'nokogiri'
140
165
  require 'base64'
@@ -162,6 +187,49 @@ class Chef
162
187
  end
163
188
  end
164
189
 
190
+ def get_azure_profile_file_path
191
+ '~/.azure/azureProfile.json'
192
+ end
193
+
194
+ def parse_azure_profile(filename, errors)
195
+ require 'openssl'
196
+ require 'uri'
197
+ azure_profile = File.read(File.expand_path(filename))
198
+ azure_profile = JSON.parse(azure_profile)
199
+ default_subscription = get_default_subscription(azure_profile)
200
+ if default_subscription.has_key?('id') && default_subscription.has_key?('managementCertificate') && default_subscription.has_key?('managementEndpointUrl')
201
+
202
+ Chef::Config[:knife][:azure_subscription_id] = default_subscription['id']
203
+ mgmt_key = OpenSSL::PKey::RSA.new(default_subscription['managementCertificate']['key']).to_pem
204
+ mgmt_cert = OpenSSL::X509::Certificate.new(default_subscription['managementCertificate']['cert']).to_pem
205
+ Chef::Config[:knife][:azure_mgmt_cert] = mgmt_key + mgmt_cert
206
+ Chef::Config[:knife][:azure_api_host_name] = URI(default_subscription['managementEndpointUrl']).host
207
+ else
208
+ errors << "Check if values set for 'id', 'managementCertificate', 'managementEndpointUrl' in -> #{filename} for 'defaultSubscription'. \n OR "
209
+ end
210
+ errors
211
+ end
212
+
213
+ def get_default_subscription(azure_profile)
214
+ first_subscription_as_default = nil
215
+ azure_profile['subscriptions'].each do |subscription|
216
+ if subscription['isDefault']
217
+ Chef::Log.info("Default subscription \'#{subscription['name']}\'' selected.")
218
+ return subscription
219
+ end
220
+
221
+ first_subscription_as_default ||= subscription
222
+ end
223
+
224
+ if first_subscription_as_default
225
+ Chef::Log.info("First subscription \'#{subscription['name']}\' selected as default.")
226
+ else
227
+ Chef::Log.info('No subscriptions found.')
228
+ exit 1
229
+ end
230
+ first_subscription_as_default
231
+ end
232
+
165
233
  def find_file(name)
166
234
  config_dir = Chef::Knife.chef_config_dir
167
235
  if File.exist? name
@@ -176,7 +244,6 @@ class Chef
176
244
  end
177
245
  file
178
246
  end
179
-
180
247
  end
181
248
  end
182
249
  end
@@ -18,7 +18,6 @@
18
18
  # limitations under the License.
19
19
  #
20
20
 
21
- require 'highline'
22
21
  require File.expand_path('../azure_base', __FILE__)
23
22
 
24
23
  class Chef
@@ -35,18 +34,15 @@ class Chef
35
34
  :boolean => true,
36
35
  :description => "Show all the fields of the images"
37
36
 
38
- def h
39
- @highline ||= HighLine.new
40
- end
41
37
 
42
38
  def run
43
39
  $stdout.sync = true
44
40
 
45
- validate!
41
+ validate_asm_keys!
42
+ items = service.list_images
46
43
 
47
44
  image_labels = !locate_config_value(:show_all_fields) ? ['Name', 'OS', 'Location'] : ['Name', 'Category', 'Label', 'OS', 'Location']
48
45
  image_list = image_labels.map {|label| ui.color(label, :bold)}
49
- items = connection.images.all
50
46
 
51
47
  image_items = image_labels.map {|item| item.downcase }
52
48
  items.each do |image|
@@ -54,7 +50,7 @@ class Chef
54
50
  end
55
51
 
56
52
  puts "\n"
57
- puts h.list(image_list, :uneven_columns_across, !locate_config_value(:show_all_fields) ? 3 : 5)
53
+ puts ui.list(image_list, :uneven_columns_across, !locate_config_value(:show_all_fields) ? 3 : 5)
58
54
 
59
55
  end
60
56
  end
@@ -47,10 +47,7 @@ class Chef
47
47
  $stdout.sync = true
48
48
 
49
49
  Chef::Log.info('validating...')
50
- validate!([:azure_subscription_id,
51
- :azure_mgmt_cert,
52
- :azure_api_host_name,
53
- :azure_load_balancer])
50
+ validate_asm_keys!(:azure_load_balancer)
54
51
 
55
52
  params = {
56
53
  azure_load_balancer: locate_config_value(:azure_load_balancer),
@@ -59,7 +56,7 @@ class Chef
59
56
  azure_dns_name: locate_config_value(:azure_dns_name)
60
57
  }
61
58
 
62
- rsp = connection.lbs.create(params)
59
+ rsp = service.create_internal_lb(params)
63
60
  print "\n"
64
61
  if rsp.at_css('Status').nil?
65
62
  if rsp.at_css('Code').nil? || rsp.at_css('Message').nil?
@@ -27,24 +27,10 @@ class Chef
27
27
 
28
28
  banner 'knife azure internal lb list (options)'
29
29
 
30
- def hl
31
- @highline ||= HighLine.new
32
- end
33
-
34
30
  def run
35
31
  $stdout.sync = true
36
-
37
- validate!
38
-
39
- cols = %w{Name Service Subnet VIP}
40
-
41
- the_list = cols.map { |col| ui.color(col, :bold) }
42
- connection.lbs.all.each do |lb|
43
- cols.each { |attr| the_list << lb.send(attr.downcase).to_s }
44
- end
45
-
46
- puts "\n"
47
- puts hl.list(the_list, :uneven_columns_across, cols.size)
32
+ validate_asm_keys!
33
+ service.list_internal_lb
48
34
  end
49
35
  end
50
36
  end
@@ -20,8 +20,9 @@
20
20
 
21
21
  require 'chef/knife/azure_base'
22
22
  require 'chef/knife/winrm_base'
23
- require 'chef/knife/bootstrap_windows_base'
24
23
  require 'securerandom'
24
+ require 'chef/knife/bootstrap/bootstrap_options'
25
+ require 'chef/knife/bootstrap/bootstrapper'
25
26
 
26
27
  class Chef
27
28
  class Knife
@@ -29,7 +30,8 @@ class Chef
29
30
 
30
31
  include Knife::AzureBase
31
32
  include Knife::WinrmBase
32
- include Knife::BootstrapWindowsBase
33
+ include Knife::Bootstrap::BootstrapOptions
34
+ include Knife::Bootstrap::Bootstrapper
33
35
 
34
36
  deps do
35
37
  require 'readline'
@@ -40,32 +42,16 @@ class Chef
40
42
  Chef::Knife::Bootstrap.load_deps
41
43
  end
42
44
 
43
- def load_winrm_deps
44
- require 'winrm'
45
- require 'chef/knife/winrm'
46
- require 'chef/knife/bootstrap_windows_winrm'
47
- end
48
-
49
45
  banner "knife azure server create (options)"
50
46
 
51
47
  attr_accessor :initial_sleep_delay
52
48
 
53
- option :forward_agent,
54
- :short => "-A",
55
- :long => "--forward-agent",
56
- :description => "Enable SSH agent forwarding",
57
- :boolean => true
58
49
 
59
50
  option :bootstrap_protocol,
60
51
  :long => "--bootstrap-protocol protocol",
61
52
  :description => "Protocol to bootstrap windows servers. options: 'winrm' or 'ssh' or 'cloud-api'.",
62
53
  :default => "winrm"
63
54
 
64
- option :chef_node_name,
65
- :short => "-N NAME",
66
- :long => "--node-name NAME",
67
- :description => "The Chef node name for your new node"
68
-
69
55
  option :ssh_user,
70
56
  :short => "-x USERNAME",
71
57
  :long => "--ssh-user USERNAME",
@@ -81,49 +67,6 @@ class Chef
81
67
  :long => "--ssh-port PORT",
82
68
  :description => "The ssh port. Default is 22. If --azure-connect-to-existing-dns set then default SSH port is random"
83
69
 
84
- option :prerelease,
85
- :long => "--prerelease",
86
- :description => "Install the pre-release chef gems"
87
-
88
- option :bootstrap_version,
89
- :long => "--bootstrap-version VERSION",
90
- :description => "The version of Chef to install",
91
- :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
92
-
93
- option :distro,
94
- :short => "-d DISTRO",
95
- :long => "--distro DISTRO",
96
- :description => "Bootstrap a distro using a template. [DEPRECATED] Use --bootstrap-template option instead.",
97
- :proc => Proc.new { |v|
98
- Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use --bootstrap-template option instead.")
99
- v
100
- }
101
-
102
- option :template_file,
103
- :long => "--template-file TEMPLATE",
104
- :description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.",
105
- :proc => Proc.new { |v|
106
- Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.")
107
- v
108
- }
109
-
110
- option :bootstrap_template,
111
- :long => "--bootstrap-template TEMPLATE",
112
- :description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
113
-
114
- option :run_list,
115
- :short => "-r RUN_LIST",
116
- :long => "--run-list RUN_LIST",
117
- :description => "Comma separated list of roles/recipes to apply",
118
- :proc => lambda { |o| o.split(/[\s,]+/) },
119
- :default => []
120
-
121
- option :host_key_verify,
122
- :long => "--[no-]host-key-verify",
123
- :description => "Verify host key, enabled by default.",
124
- :boolean => true,
125
- :default => true
126
-
127
70
  option :node_ssl_verify_mode,
128
71
  :long => "--node-ssl-verify-mode [peer|none]",
129
72
  :description => "Whether or not to verify the SSL cert for all HTTPS requests.",
@@ -139,60 +82,6 @@ class Chef
139
82
  :description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
140
83
  :boolean => true
141
84
 
142
- option :bootstrap_proxy,
143
- :long => "--bootstrap-proxy PROXY_URL",
144
- :description => "The proxy server for the node being bootstrapped",
145
- :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
146
-
147
- option :bootstrap_no_proxy,
148
- :long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
149
- :description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
150
- :proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
151
-
152
- option :bootstrap_url,
153
- :long => "--bootstrap-url URL",
154
- :description => "URL to a custom installation script",
155
- :proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u }
156
-
157
- option :bootstrap_install_command,
158
- :long => "--bootstrap-install-command COMMANDS",
159
- :description => "Custom command to install chef-client",
160
- :proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
161
-
162
- option :bootstrap_wget_options,
163
- :long => "--bootstrap-wget-options OPTIONS",
164
- :description => "Add options to wget when installing chef-client",
165
- :proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
166
-
167
- option :bootstrap_curl_options,
168
- :long => "--bootstrap-curl-options OPTIONS",
169
- :description => "Add options to curl when install chef-client",
170
- :proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co }
171
-
172
- option :bootstrap_vault_file,
173
- :long => '--bootstrap-vault-file VAULT_FILE',
174
- :description => 'A JSON file with a list of vault(s) and item(s) to be updated'
175
-
176
- option :bootstrap_vault_json,
177
- :long => '--bootstrap-vault-json VAULT_JSON',
178
- :description => 'A JSON string with the vault(s) and item(s) to be updated'
179
-
180
- option :bootstrap_vault_item,
181
- :long => '--bootstrap-vault-item VAULT_ITEM',
182
- :description => 'A single vault and item to update as "vault:item"',
183
- :proc => Proc.new { |i|
184
- (vault, item) = i.split(/:/)
185
- Chef::Config[:knife][:bootstrap_vault_item] ||= {}
186
- Chef::Config[:knife][:bootstrap_vault_item][vault] ||= []
187
- Chef::Config[:knife][:bootstrap_vault_item][vault].push(item)
188
- Chef::Config[:knife][:bootstrap_vault_item]
189
- }
190
-
191
- option :use_sudo_password,
192
- :long => "--use-sudo-password",
193
- :description => "Execute the bootstrap via sudo with password",
194
- :boolean => false
195
-
196
85
  option :azure_storage_account,
197
86
  :short => "-a NAME",
198
87
  :long => "--azure-storage-account NAME",
@@ -211,7 +100,8 @@ class Chef
211
100
  :short => "-m LOCATION",
212
101
  :long => "--azure-service-location LOCATION",
213
102
  :description => "Required if not using an Affinity Group. Specifies the geographic location - the name of the data center location that is valid for your subscription.
214
- Eg: West US, East US, East Asia, Southeast Asia, North Europe, West Europe"
103
+ Eg: West US, East US, East Asia, Southeast Asia, North Europe, West Europe",
104
+ :proc => Proc.new { |lo| Chef::Config[:knife][:azure_service_location] = lo }
215
105
 
216
106
  option :azure_affinity_group,
217
107
  :short => "-a GROUP",
@@ -242,7 +132,8 @@ class Chef
242
132
  :short => "-z SIZE",
243
133
  :long => "--azure-vm-size SIZE",
244
134
  :description => "Optional. Size of virtual machine (ExtraSmall, Small, Medium, Large, ExtraLarge)",
245
- :default => 'Small'
135
+ :default => 'Small',
136
+ :proc => Proc.new { |si| Chef::Config[:knife][:azure_vm_size] = si }
246
137
 
247
138
  option :azure_availability_set,
248
139
  :long => "--azure-availability-set NAME",
@@ -297,21 +188,6 @@ class Chef
297
188
  :long => "--identity-file-passphrase PASSWORD",
298
189
  :description => "SSH key passphrase. Optional, specify if passphrase for identity-file exists"
299
190
 
300
- option :hint,
301
- :long => "--hint HINT_NAME[=HINT_FILE]",
302
- :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
303
- :proc => Proc.new { |h|
304
- Chef::Config[:knife][:hints] ||= {}
305
- name, path = h.split("=")
306
- Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
307
- }
308
-
309
- option :json_attributes,
310
- :short => "-j JSON",
311
- :long => "--json-attributes JSON",
312
- :description => "A JSON string to be added to the first run of chef-client",
313
- :proc => lambda { |o| JSON.parse(o) }
314
-
315
191
  option :thumbprint,
316
192
  :long => "--thumbprint THUMBPRINT",
317
193
  :description => "The thumprint of the ssl certificate"
@@ -324,12 +200,6 @@ class Chef
324
200
  :long => "--cert-path PATH",
325
201
  :description => "SSL Certificate Path"
326
202
 
327
- option :auto_update_client,
328
- :long => "--auto-update-client",
329
- :boolean => true,
330
- :default => false,
331
- :description => "Set this flag to enable auto chef client update in azure chef extension. This flag should be used with cloud-api bootstrap protocol only"
332
-
333
203
  option :winrm_max_timeout,
334
204
  :long => "--winrm-max-timeout MINUTES",
335
205
  :description => "Set winrm maximum command timeout in minutes, useful for long bootstraps"
@@ -338,12 +208,6 @@ class Chef
338
208
  :long => "--winrm-max-memory-per-shell",
339
209
  :description => "Set winrm max memory per shell in MB"
340
210
 
341
- option :delete_chef_extension_config,
342
- :long => "--delete-chef-extension-config",
343
- :boolean => true,
344
- :default => false,
345
- :description => "Determines whether Chef configuration files removed when Azure removes the Chef resource extension from the VM. This option is only valid for the 'cloud-api' bootstrap protocol. The default is false."
346
-
347
211
  option :azure_domain_name,
348
212
  :long => "--azure-domain-name DOMAIN_NAME",
349
213
  :description => "Optional. Specifies the domain name to join. If the domains name is not specified, --azure-domain-user must specify the user principal name (UPN) format (user@fully-qualified-DNS-domain) or the fully-qualified-DNS-domain\\username format"
@@ -366,20 +230,9 @@ class Chef
366
230
  :long => "--azure-extension-client-config CLIENT_PATH",
367
231
  :description => "Optional. Path to a client.rb file for use by the bootstrapped node. Only honored when --bootstrap-protocol is set to `cloud-api`."
368
232
 
369
- def strip_non_ascii(string)
370
- string.gsub(/[^0-9a-z ]/i, '')
371
- end
372
-
373
- def random_string(len=10)
374
- (0...len).map{65.+(rand(25)).chr}.join
375
- end
376
-
377
233
  def wait_until_virtual_machine_ready(retry_interval_in_seconds = 30)
378
-
379
234
  vm_status = nil
380
235
 
381
- puts
382
-
383
236
  begin
384
237
  azure_vm_startup_timeout = locate_config_value(:azure_vm_startup_timeout).to_i
385
238
  azure_vm_ready_timeout = locate_config_value(:azure_vm_ready_timeout).to_i
@@ -393,7 +246,7 @@ class Chef
393
246
  end
394
247
  end
395
248
 
396
- msg_server_summary(get_role_server())
249
+ msg_server_summary(service.get_role_server(locate_config_value(:azure_dns_name), locate_config_value(:azure_vm_name)))
397
250
 
398
251
  if locate_config_value(:bootstrap_protocol) == "cloud-api"
399
252
  extension_status = wait_for_resource_extension_state(:wagent_provisioning, 5, retry_interval_in_seconds)
@@ -477,7 +330,7 @@ class Chef
477
330
  end
478
331
 
479
332
  def get_virtual_machine_status()
480
- role = get_role_server()
333
+ role = service.get_role_server(locate_config_value(:azure_dns_name), locate_config_value(:azure_vm_name))
481
334
  unless role.nil?
482
335
  Chef::Log.debug("Role status is #{role.status.to_s}")
483
336
  if "ReadyRole".eql? role.status.to_s
@@ -492,8 +345,8 @@ class Chef
492
345
  end
493
346
 
494
347
  def get_extension_status()
495
- deployment_name = connection.deploys.get_deploy_name_for_hostedservice(locate_config_value(:azure_dns_name))
496
- deployment = connection.query_azure("hostedservices/#{locate_config_value(:azure_dns_name)}/deployments/#{deployment_name}")
348
+ deployment_name = service.deployment_name(locate_config_value(:azure_dns_name))
349
+ deployment = service.deployment("hostedservices/#{locate_config_value(:azure_dns_name)}/deployments/#{deployment_name}")
497
350
  extension_status = Hash.new
498
351
 
499
352
  if deployment.at_css('Deployment Name') != nil
@@ -538,279 +391,130 @@ class Chef
538
391
  return extension_status
539
392
  end
540
393
 
541
- def get_role_server
542
- deploy = connection.deploys.queryDeploy(locate_config_value(:azure_dns_name))
543
- deploy.find_role(locate_config_value(:azure_vm_name))
394
+ def fetch_role
395
+ deployment_name = service.deployment_name(locate_config_value(:azure_dns_name))
396
+ deployment = service.deployment("hostedservices/#{locate_config_value(:azure_dns_name)}/deployments/#{deployment_name}")
397
+
398
+ if deployment.at_css('Deployment Name') != nil
399
+ role_list_xml = deployment.css('RoleInstanceList RoleInstance')
400
+ role_list_xml.each do |role|
401
+ if role.at_css("RoleName").text == locate_config_value(:azure_vm_name)
402
+ return role
403
+ end
404
+ end
405
+ end
406
+ return nil
407
+ end
408
+
409
+ def fetch_extension(role)
410
+ ext_list_xml = role.css("ResourceExtensionStatusList ResourceExtensionStatus")
411
+ ext_list_xml.each do |ext|
412
+ if ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.LinuxChefClient" || ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.ChefClient"
413
+ return ext
414
+ end
415
+ end
416
+ return nil
544
417
  end
545
418
 
546
- def tcp_test_winrm(ip_addr, port)
547
- hostname = ip_addr
548
- socket = TCPSocket.new(hostname, port)
549
- return true
550
- rescue SocketError
551
- sleep 2
552
- false
553
- rescue Errno::ETIMEDOUT
554
- false
555
- rescue Errno::EPERM
556
- false
557
- rescue Errno::ECONNREFUSED
558
- sleep 2
559
- false
560
- rescue Errno::EHOSTUNREACH
561
- sleep 2
562
- false
563
- rescue Errno::ENETUNREACH
564
- sleep 2
565
- false
419
+ def fetch_substatus(extension)
420
+ return nil if extension.at_css("ExtensionSettingStatus SubStatusList SubStatus").nil?
421
+ substatus_list_xml = extension.css("ExtensionSettingStatus SubStatusList SubStatus")
422
+ substatus_list_xml.each do |substatus|
423
+ if substatus.at_css("Name").text == "Chef Client run logs"
424
+ return substatus
425
+ end
426
+ end
427
+ return nil
566
428
  end
567
429
 
568
- def tcp_test_ssh(fqdn, sshport)
569
- tcp_socket = TCPSocket.new(fqdn, sshport)
570
- readable = IO.select([tcp_socket], nil, nil, 5)
571
- if readable
572
- Chef::Log.debug("sshd accepting connections on #{fqdn}, banner is #{tcp_socket.gets}")
573
- yield
574
- true
430
+ def fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout)
431
+ ## fetch server details ##
432
+ role = fetch_role
433
+ if role != nil
434
+ ## fetch Chef Extension details deployed on the server ##
435
+ ext = fetch_extension(role)
436
+ if ext != nil
437
+ ## fetch substatus field which contains the chef-client run logs ##
438
+ substatus = fetch_substatus(ext)
439
+ if substatus != nil
440
+ ## chef-client run logs becomes available ##
441
+ name = substatus.at_css("Name").text
442
+ status = substatus.at_css("Status").text
443
+ message = substatus.at_css("Message").text
444
+
445
+ ## printing the logs ##
446
+ puts "\n\n******** Please find the chef-client run details below ********\n\n"
447
+ print "----> chef-client run status: "
448
+ case status
449
+ when "Success"
450
+ ## chef-client run succeeded ##
451
+ color = :green
452
+ when "Error"
453
+ ## chef-client run failed ##
454
+ color = :red
455
+ when "Transitioning"
456
+ ## chef-client run did not complete within maximum timeout of 30 minutes ##
457
+ ## fetch whatever logs available under the chef-client.log file ##
458
+ color = :yellow
459
+ end
460
+ puts "#{ui.color(status, color, :bold)}"
461
+ puts "----> chef-client run logs: "
462
+ puts "\n#{message}\n" ## message field of substatus contains the chef-client run logs ##
463
+ else
464
+ ## unavailability of the substatus field indicates that chef-client run is not completed yet on the server ##
465
+ fetch_process_wait_time = ((Time.now - fetch_process_start_time) / 60).round
466
+ if fetch_process_wait_time <= fetch_process_wait_timeout ## wait for maximum 30 minutes until chef-client run logs becomes available ##
467
+ print "#{ui.color('.', :bold)}"
468
+ sleep 30
469
+ fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout)
470
+ else
471
+ ## wait time exceeded 30 minutes timeout ##
472
+ ui.error "\nchef-client run logs could not be fetched since fetch process exceeded wait timeout of #{fetch_process_wait_timeout} minutes.\n"
473
+ end
474
+ end
475
+ else
476
+ ## Chef Extension could not be found ##
477
+ ui.error("Unable to find Chef extension under role #{locate_config_value(:azure_vm_name)}.")
478
+ end
575
479
  else
576
- false
480
+ ## server could not be found ##
481
+ ui.error("chef-client run logs could not be fetched since role #{locate_config_value(:azure_vm_name)} could not be found.")
577
482
  end
578
- rescue SocketError
579
- sleep 2
580
- false
581
- rescue Errno::ETIMEDOUT
582
- false
583
- rescue Errno::EPERM
584
- false
585
- rescue Errno::ECONNREFUSED
586
- sleep 2
587
- false
588
- rescue Errno::EHOSTUNREACH
589
- sleep 2
590
- false
591
- ensure
592
- tcp_socket && tcp_socket.close
593
483
  end
594
484
 
595
485
  def run
596
486
  $stdout.sync = true
487
+
597
488
  storage = nil
598
489
 
599
490
  Chef::Log.info("validating...")
600
- validate!
491
+ validate_asm_keys!(:azure_source_image)
601
492
 
602
- if (locate_config_value(:auto_update_client) || locate_config_value(:delete_chef_extension_config)) && (locate_config_value(:bootstrap_protocol) != 'cloud-api')
603
- ui.error("--auto-update-client option works with --bootstrap-protocol cloud-api") if locate_config_value(:auto_update_client)
604
- ui.error("--delete-chef-extension-config option works with --bootstrap-protocol cloud-api") if locate_config_value(:delete_chef_extension_config)
605
- exit 1
606
- end
493
+ validate_params!
607
494
 
608
- ssh_override_winrm if %w(ssh cloud-api).include?(locate_config_value(:bootstrap_protocol)) and !is_image_windows?
495
+ ssh_override_winrm if !is_image_windows?
609
496
 
610
497
  Chef::Log.info("creating...")
611
498
 
612
499
  config[:azure_dns_name] = get_dns_name(locate_config_value(:azure_dns_name))
500
+
613
501
  if not locate_config_value(:azure_vm_name)
614
502
  config[:azure_vm_name] = locate_config_value(:azure_dns_name)
615
503
  end
616
504
 
617
- remove_hosted_service_on_failure = locate_config_value(:azure_dns_name)
618
- if connection.hosts.exists?(locate_config_value(:azure_dns_name))
619
- remove_hosted_service_on_failure = nil
620
- end
621
-
622
- #If Storage Account is not specified, check if the geographic location has one to re-use
623
- if not locate_config_value(:azure_storage_account)
624
- storage_accts = connection.storageaccounts.all
625
- storage = storage_accts.find { |storage_acct| storage_acct.location.to_s == locate_config_value(:azure_service_location) }
626
- if not storage
627
- config[:azure_storage_account] = [strip_non_ascii(locate_config_value(:azure_vm_name)), random_string].join.downcase
628
- remove_storage_service_on_failure = config[:azure_storage_account]
629
- else
630
- remove_storage_service_on_failure = nil
631
- config[:azure_storage_account] = storage.name.to_s
632
- end
633
- else
634
- if connection.storageaccounts.exists?(locate_config_value(:azure_storage_account))
635
- remove_storage_service_on_failure = nil
636
- else
637
- remove_storage_service_on_failure = locate_config_value(:azure_storage_account)
638
- end
505
+ service.create_server(create_server_def)
506
+ wait_until_virtual_machine_ready()
507
+ if locate_config_value(:bootstrap_protocol) == 'cloud-api' && locate_config_value(:extended_logs)
508
+ print "\n\nWaiting for the first chef-client run"
509
+ fetch_chef_client_logs(Time.now, 30)
639
510
  end
640
-
641
- begin
642
- connection.deploys.create(create_server_def)
643
- wait_until_virtual_machine_ready()
644
- server = get_role_server()
645
- rescue Exception => e
646
- Chef::Log.error("Failed to create the server -- exception being rescued: #{e.to_s}")
647
- backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
648
- Chef::Log.debug("#{backtrace_message}")
649
- cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
650
- end
651
-
511
+ server = service.get_role_server(locate_config_value(:azure_dns_name), locate_config_value(:azure_vm_name))
652
512
  msg_server_summary(server)
653
513
 
654
514
  bootstrap_exec(server) unless locate_config_value(:bootstrap_protocol) == 'cloud-api'
655
515
  end
656
516
 
657
- def default_bootstrap_template
658
- is_image_windows? ? 'windows-chef-client-msi' : 'chef-full'
659
- end
660
-
661
- def bootstrap_exec(server)
662
- fqdn = server.publicipaddress
663
-
664
- if is_image_windows?
665
- if locate_config_value(:bootstrap_protocol) == 'ssh'
666
- port = server.sshport
667
- print "#{ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)}"
668
-
669
- print(".") until tcp_test_ssh(fqdn,port) {
670
- sleep @initial_sleep_delay ||= 10
671
- puts("done")
672
- }
673
-
674
- elsif locate_config_value(:bootstrap_protocol) == 'winrm'
675
- port = server.winrmport
676
-
677
- print "#{ui.color("Waiting for winrm on #{fqdn}:#{port}", :magenta)}"
678
-
679
- print(".") until tcp_test_winrm(fqdn,port) {
680
- sleep @initial_sleep_delay ||= 10
681
- puts("done")
682
- }
683
- end
684
-
685
- puts("\n")
686
- bootstrap_for_windows_node(server,fqdn, port).run
687
- else
688
- unless server && server.publicipaddress && server.sshport
689
- Chef::Log.fatal("server not created")
690
- exit 1
691
- end
692
-
693
- port = server.sshport
694
-
695
- print ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)
696
-
697
- print(".") until tcp_test_ssh(fqdn,port) {
698
- sleep @initial_sleep_delay ||= 10
699
- puts("done")
700
- }
701
-
702
- puts("\n")
703
- bootstrap_for_node(server,fqdn,port).run
704
- end
705
-
706
- msg_server_summary(server)
707
- end
708
-
709
- def load_cloud_attributes_in_hints(server)
710
- # Modify global configuration state to ensure hint gets set by knife-bootstrap
711
- # Query azure and load necessary attributes.
712
- cloud_attributes = {}
713
- cloud_attributes["public_ip"] = server.publicipaddress
714
- cloud_attributes["vm_name"] = server.name
715
- cloud_attributes["public_fqdn"] = server.hostedservicename.to_s + ".cloudapp.net"
716
- cloud_attributes["public_ssh_port"] = server.sshport if server.sshport
717
- cloud_attributes["public_winrm_port"] = server.winrmport if server.winrmport
718
-
719
- Chef::Config[:knife][:hints] ||= {}
720
- Chef::Config[:knife][:hints]["azure"] ||= cloud_attributes
721
-
722
- end
723
-
724
- def bootstrap_common_params(bootstrap, server)
725
-
726
- bootstrap.config[:run_list] = config[:run_list]
727
- bootstrap.config[:prerelease] = config[:prerelease]
728
- bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
729
- bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
730
- bootstrap.config[:distro] = locate_config_value(:distro) || default_bootstrap_template
731
- # setting bootstrap_template value to template_file for backward
732
- bootstrap.config[:template_file] = locate_config_value(:template_file) || locate_config_value(:bootstrap_template)
733
- bootstrap.config[:node_ssl_verify_mode] = locate_config_value(:node_ssl_verify_mode)
734
- bootstrap.config[:node_verify_api_cert] = locate_config_value(:node_verify_api_cert)
735
- bootstrap.config[:bootstrap_no_proxy] = locate_config_value(:bootstrap_no_proxy)
736
- bootstrap.config[:bootstrap_url] = locate_config_value(:bootstrap_url)
737
- bootstrap.config[:bootstrap_vault_file] = locate_config_value(:bootstrap_vault_file)
738
- bootstrap.config[:bootstrap_vault_json] = locate_config_value(:bootstrap_vault_json)
739
- bootstrap.config[:bootstrap_vault_item] = locate_config_value(:bootstrap_vault_item)
740
-
741
- load_cloud_attributes_in_hints(server)
742
- bootstrap
743
- end
744
-
745
- def bootstrap_for_windows_node(server, fqdn, port)
746
- if locate_config_value(:bootstrap_protocol) == 'winrm'
747
-
748
- load_winrm_deps
749
- if not Chef::Platform.windows?
750
- require 'gssapi'
751
- end
752
-
753
- bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
754
-
755
- bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || 'Administrator'
756
- bootstrap.config[:winrm_password] = locate_config_value(:winrm_password)
757
- bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
758
- bootstrap.config[:winrm_authentication_protocol] = locate_config_value(:winrm_authentication_protocol)
759
- bootstrap.config[:winrm_port] = port
760
- bootstrap.config[:auth_timeout] = locate_config_value(:auth_timeout)
761
- # Todo: we should skip cert generate in case when winrm_ssl_verify_mode=verify_none
762
- bootstrap.config[:winrm_ssl_verify_mode] = locate_config_value(:winrm_ssl_verify_mode)
763
- elsif locate_config_value(:bootstrap_protocol) == 'ssh'
764
- bootstrap = Chef::Knife::BootstrapWindowsSsh.new
765
- bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
766
- bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
767
- bootstrap.config[:forward_agent] = locate_config_value(:forward_agent)
768
- bootstrap.config[:ssh_port] = port
769
- bootstrap.config[:identity_file] = locate_config_value(:identity_file)
770
- bootstrap.config[:host_key_verify] = locate_config_value(:host_key_verify)
771
- else
772
- ui.error("Unsupported Bootstrapping Protocol. Supported : winrm, ssh")
773
- exit 1
774
- end
775
- bootstrap.name_args = [fqdn]
776
- bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.name
777
- bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
778
- bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
779
- bootstrap.config[:msi_url] = locate_config_value(:msi_url)
780
- bootstrap.config[:install_as_service] = locate_config_value(:install_as_service)
781
- bootstrap_common_params(bootstrap, server)
782
- end
783
-
784
- def bootstrap_for_node(server,fqdn,port)
785
- bootstrap = Chef::Knife::Bootstrap.new
786
- bootstrap.name_args = [fqdn]
787
- bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
788
- bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
789
- bootstrap.config[:ssh_port] = port
790
- bootstrap.config[:identity_file] = locate_config_value(:identity_file)
791
- bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || server.name
792
- bootstrap.config[:use_sudo] = true unless locate_config_value(:ssh_user) == 'root'
793
- bootstrap.config[:use_sudo_password] = true if bootstrap.config[:use_sudo]
794
- bootstrap.config[:environment] = locate_config_value(:environment)
795
- # may be needed for vpc_mode
796
- bootstrap.config[:host_key_verify] = config[:host_key_verify]
797
- bootstrap.config[:secret] = locate_config_value(:secret)
798
- bootstrap.config[:secret_file] = locate_config_value(:secret_file)
799
- bootstrap.config[:bootstrap_install_command] = locate_config_value(:bootstrap_install_command)
800
- bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
801
- bootstrap.config[:bootstrap_curl_options] = locate_config_value(:bootstrap_curl_options)
802
- bootstrap_common_params(bootstrap, server)
803
- end
804
-
805
- def validate!
806
- super([
807
- :azure_subscription_id,
808
- :azure_mgmt_cert,
809
- :azure_api_host_name,
810
- :azure_source_image,
811
- :azure_vm_size,
812
- ])
813
-
517
+ def validate_params!
814
518
  if locate_config_value(:winrm_password) and (locate_config_value(:winrm_password).length <= 6 and locate_config_value(:winrm_password).length >= 72)
815
519
  ui.error("The supplied password must be 6-72 characters long and meet password complexity requirements")
816
520
  exit 1
@@ -839,7 +543,7 @@ class Chef
839
543
  exit 1
840
544
  end
841
545
 
842
- if !(connection.images.exists?(locate_config_value(:azure_source_image)))
546
+ if !(service.valid_image?(locate_config_value(:azure_source_image)))
843
547
  ui.error("Image provided is invalid")
844
548
  exit 1
845
549
  end
@@ -856,6 +560,18 @@ class Chef
856
560
  ui.error("The SSL transport was specified without the --thumbprint option. Specify a thumbprint, or alternatively set the --winrm-ssl-verify-mode option to 'verify_none' to skip verification.")
857
561
  exit 1
858
562
  end
563
+
564
+ if (locate_config_value(:auto_update_client) || locate_config_value(:delete_chef_extension_config) || locate_config_value(:uninstall_chef_client)) && (locate_config_value(:bootstrap_protocol) != 'cloud-api')
565
+ ui.error("--auto-update-client option works with --bootstrap-protocol cloud-api") if locate_config_value(:auto_update_client)
566
+ ui.error("--delete-chef-extension-config option works with --bootstrap-protocol cloud-api") if locate_config_value(:delete_chef_extension_config)
567
+ ui.error("--uninstall-chef-client option works with --bootstrap-protocol cloud-api") if locate_config_value(:uninstall_chef_client)
568
+ exit 1
569
+ end
570
+
571
+ if locate_config_value(:extended_logs) && locate_config_value(:bootstrap_protocol) != 'cloud-api'
572
+ ui.error("--extended-logs option works with --bootstrap-protocol cloud-api")
573
+ exit 1
574
+ end
859
575
  end
860
576
 
861
577
  def create_server_def
@@ -945,7 +661,7 @@ class Chef
945
661
 
946
662
  server_def[:port] = port
947
663
 
948
- server_def[:is_vm_image] = connection.images.is_vm_image(locate_config_value(:azure_source_image))
664
+ server_def[:is_vm_image] = service.vm_image?(locate_config_value(:azure_source_image))
949
665
  server_def[:azure_domain_name] = locate_config_value(:azure_domain_name) if locate_config_value(:azure_domain_name)
950
666
 
951
667
  if locate_config_value(:azure_domain_user)
@@ -969,103 +685,8 @@ class Chef
969
685
  end
970
686
  server_def[:azure_domain_passwd] = locate_config_value(:azure_domain_passwd)
971
687
  server_def[:azure_domain_ou_dn] = locate_config_value(:azure_domain_ou_dn)
972
- server_def
973
- end
974
-
975
- def get_chef_extension_name
976
- extension_name = is_image_windows? ? "ChefClient" : "LinuxChefClient"
977
- end
978
-
979
- def get_chef_extension_publisher
980
- publisher = "Chef.Bootstrap.WindowsAzure"
981
- end
982
-
983
- # get latest version
984
- def get_chef_extension_version
985
- if locate_config_value(:azure_chef_extension_version)
986
- Chef::Config[:knife][:azure_chef_extension_version]
987
- else
988
- extensions = @connection.query_azure("resourceextensions/#{get_chef_extension_publisher}/#{get_chef_extension_name}")
989
- extensions.css("Version").max.text.split(".").first + ".*"
990
- end
991
- end
992
-
993
- def get_chef_extension_public_params
994
- pub_config = Hash.new
995
- if(locate_config_value(:azure_extension_client_config))
996
- pub_config[:client_rb] = File.read(locate_config_value(:azure_extension_client_config))
997
- else
998
- pub_config[:client_rb] = "chef_server_url \t #{Chef::Config[:chef_server_url].to_json}\nvalidation_client_name\t#{Chef::Config[:validation_client_name].to_json}"
999
- end
1000
-
1001
- pub_config[:runlist] = locate_config_value(:run_list).empty? ? "" : locate_config_value(:run_list).join(",").to_json
1002
- pub_config[:autoUpdateClient] = locate_config_value(:auto_update_client) ? "true" : "false"
1003
- pub_config[:deleteChefConfig] = locate_config_value(:delete_chef_extension_config) ? "true" : "false"
1004
- pub_config[:custom_json_attr] = locate_config_value(:json_attributes) || {}
1005
-
1006
- # bootstrap attributes
1007
- pub_config[:bootstrap_options] = {}
1008
- pub_config[:bootstrap_options][:environment] = locate_config_value(:environment) if locate_config_value(:environment)
1009
- pub_config[:bootstrap_options][:chef_node_name] = config[:chef_node_name] if config[:chef_node_name]
1010
- pub_config[:bootstrap_options][:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret) if locate_config_value(:encrypted_data_bag_secret)
1011
- pub_config[:bootstrap_options][:chef_server_url] = Chef::Config[:chef_server_url] if Chef::Config[:chef_server_url]
1012
- pub_config[:bootstrap_options][:validation_client_name] = Chef::Config[:validation_client_name] if Chef::Config[:validation_client_name]
1013
- pub_config[:bootstrap_options][:node_verify_api_cert] = locate_config_value(:node_verify_api_cert) ? "true" : "false" if config.key?(:node_verify_api_cert)
1014
- pub_config[:bootstrap_options][:bootstrap_version] = locate_config_value(:bootstrap_version) if locate_config_value(:bootstrap_version)
1015
- pub_config[:bootstrap_options][:node_ssl_verify_mode] = locate_config_value(:node_ssl_verify_mode) if locate_config_value(:node_ssl_verify_mode)
1016
- pub_config[:bootstrap_options][:bootstrap_proxy] = locate_config_value(:bootstrap_proxy) if locate_config_value(:bootstrap_proxy)
1017
- Base64.encode64(pub_config.to_json)
1018
- end
1019
-
1020
- def get_chef_extension_private_params
1021
- pri_config = Hash.new
1022
688
 
1023
- # validator less bootstrap support for bootstrap protocol cloud-api
1024
- if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
1025
-
1026
- if Chef::VERSION.split('.').first.to_i == 11
1027
- ui.error('Unable to find validation key. Please verify your configuration file for validation_key config value.')
1028
- exit 1
1029
- end
1030
-
1031
- client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(
1032
- chef_config: Chef::Config,
1033
- knife_config: config,
1034
- ui: ui,
1035
- )
1036
-
1037
- client_builder.run
1038
- key_path = client_builder.client_path
1039
- pri_config[:client_pem] = File.read(key_path)
1040
- else
1041
- pri_config[:validation_key] = File.read(Chef::Config[:validation_key])
1042
- end
1043
-
1044
- # SSL cert bootstrap support
1045
- if locate_config_value(:cert_path)
1046
- if File.exist?(File.expand_path(locate_config_value(:cert_path)))
1047
- pri_config[:chef_server_crt] = File.read(locate_config_value(:cert_path))
1048
- else
1049
- ui.error('Specified SSL certificate does not exist.')
1050
- exit 1
1051
- end
1052
- end
1053
- Base64.encode64(pri_config.to_json)
1054
- end
1055
-
1056
- def cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
1057
- ui.warn("Cleaning up resources...")
1058
-
1059
- if remove_hosted_service_on_failure
1060
- ret_val = connection.hosts.delete(remove_hosted_service_on_failure)
1061
- ret_val.content.empty? ? ui.warn("Deleted created DNS: #{remove_hosted_service_on_failure}.") : ui.warn("Deletion failed for created DNS:#{remove_hosted_service_on_failure}. " + ret_val.text)
1062
- end
1063
-
1064
- if remove_storage_service_on_failure
1065
- ret_val = connection.storageaccounts.delete(remove_storage_service_on_failure)
1066
- ret_val.content.empty? ? ui.warn("Deleted created Storage Account: #{remove_storage_service_on_failure}.") : ui.warn("Deletion failed for created Storage Account: #{remove_storage_service_on_failure}. " + ret_val.text)
1067
- end
1068
- exit 1
689
+ server_def
1069
690
  end
1070
691
 
1071
692
  private