knife-azure 1.6.0.rc.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +304 -8
- data/lib/azure/azure_interface.rb +81 -0
- data/lib/azure/custom_errors.rb +35 -0
- data/lib/azure/helpers.rb +44 -0
- data/lib/azure/resource_management/ARM_base.rb +29 -0
- data/lib/azure/resource_management/ARM_deployment_template.rb +561 -0
- data/lib/azure/resource_management/ARM_interface.rb +795 -0
- data/lib/azure/resource_management/windows_credentials.rb +136 -0
- data/lib/azure/service_management/ASM_interface.rb +301 -0
- data/lib/azure/{ag.rb → service_management/ag.rb} +2 -2
- data/lib/azure/{certificate.rb → service_management/certificate.rb} +2 -2
- data/lib/azure/service_management/connection.rb +102 -0
- data/lib/azure/{deploy.rb → service_management/deploy.rb} +8 -2
- data/lib/azure/{disk.rb → service_management/disk.rb} +2 -2
- data/lib/azure/{host.rb → service_management/host.rb} +2 -2
- data/lib/azure/{image.rb → service_management/image.rb} +2 -2
- data/lib/azure/{loadbalancer.rb → service_management/loadbalancer.rb} +4 -18
- data/lib/azure/{rest.rb → service_management/rest.rb} +15 -10
- data/lib/azure/{role.rb → service_management/role.rb} +174 -6
- data/lib/azure/{storageaccount.rb → service_management/storageaccount.rb} +2 -2
- data/lib/azure/{utility.rb → service_management/utility.rb} +0 -0
- data/lib/azure/{vnet.rb → service_management/vnet.rb} +2 -2
- data/lib/chef/knife/azure_ag_create.rb +3 -6
- data/lib/chef/knife/azure_ag_list.rb +2 -16
- data/lib/chef/knife/azure_base.rb +89 -22
- data/lib/chef/knife/azure_image_list.rb +3 -7
- data/lib/chef/knife/azure_internal-lb_create.rb +2 -5
- data/lib/chef/knife/azure_internal-lb_list.rb +2 -16
- data/lib/chef/knife/azure_server_create.rb +122 -501
- data/lib/chef/knife/azure_server_delete.rb +15 -38
- data/lib/chef/knife/azure_server_list.rb +2 -27
- data/lib/chef/knife/azure_server_show.rb +4 -60
- data/lib/chef/knife/azure_vnet_create.rb +2 -7
- data/lib/chef/knife/azure_vnet_list.rb +2 -17
- data/lib/chef/knife/azurerm_base.rb +228 -0
- data/lib/chef/knife/azurerm_server_create.rb +393 -0
- data/lib/chef/knife/azurerm_server_delete.rb +121 -0
- data/lib/chef/knife/azurerm_server_list.rb +18 -0
- data/lib/chef/knife/azurerm_server_show.rb +37 -0
- data/lib/chef/knife/bootstrap/bootstrap_options.rb +105 -0
- data/lib/chef/knife/bootstrap/bootstrapper.rb +343 -0
- data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +116 -0
- data/lib/chef/knife/bootstrap_azure.rb +110 -0
- data/lib/chef/knife/bootstrap_azurerm.rb +116 -0
- data/lib/knife-azure/version.rb +1 -2
- metadata +132 -16
- data/lib/azure/connection.rb +0 -99
@@ -0,0 +1,136 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Nimisha Sharad (nimisha.sharad@clogeny.com)
|
3
|
+
# Copyright:: Copyright (c) 2015-2016 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
|
+
# XPLAT stores the access token and other information in windows credential manager.
|
20
|
+
# Using FFI to call CredRead function
|
21
|
+
require 'chef'
|
22
|
+
require 'mixlib/shellout'
|
23
|
+
require 'ffi'
|
24
|
+
|
25
|
+
module Azure::ARM
|
26
|
+
|
27
|
+
module ReadCred
|
28
|
+
|
29
|
+
extend FFI::Library
|
30
|
+
|
31
|
+
ffi_lib 'Advapi32'
|
32
|
+
|
33
|
+
CRED_TYPE_GENERIC = 1
|
34
|
+
CRED_TYPE_DOMAIN_PASSWORD = 2
|
35
|
+
CRED_TYPE_DOMAIN_CERTIFICATE = 3
|
36
|
+
CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 4
|
37
|
+
|
38
|
+
# Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
|
39
|
+
class FILETIME < FFI::Struct
|
40
|
+
layout :dwLowDateTime, :uint32,
|
41
|
+
:dwHighDateTime, :uint32
|
42
|
+
end
|
43
|
+
|
44
|
+
# Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374790(v=vs.85).aspx
|
45
|
+
class CREDENTIAL_ATTRIBUTE < FFI::Struct
|
46
|
+
layout :Keyword, :pointer,
|
47
|
+
:Flags, :uint32,
|
48
|
+
:ValueSize, :uint32,
|
49
|
+
:Value, :pointer
|
50
|
+
end
|
51
|
+
|
52
|
+
# Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx
|
53
|
+
class CREDENTIAL_OBJECT < FFI::Struct
|
54
|
+
layout :Flags, :uint32,
|
55
|
+
:Type, :uint32,
|
56
|
+
:TargetName, :pointer,
|
57
|
+
:Comment, :pointer,
|
58
|
+
:LastWritten, FILETIME,
|
59
|
+
:CredentialBlobSize, :uint32,
|
60
|
+
:CredentialBlob, :pointer,
|
61
|
+
:Persist, :uint32,
|
62
|
+
:AttributeCount, :uint32,
|
63
|
+
:Attributes, CREDENTIAL_ATTRIBUTE,
|
64
|
+
:TargetAlias, :pointer,
|
65
|
+
:UserName, :pointer
|
66
|
+
end
|
67
|
+
|
68
|
+
# Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374804(v=vs.85).aspx
|
69
|
+
attach_function :CredReadW, [:pointer, :uint32, :uint32, :pointer], :bool
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
module WindowsCredentials
|
74
|
+
|
75
|
+
include Chef::Mixin::WideString
|
76
|
+
include ReadCred
|
77
|
+
|
78
|
+
def token_details_for_windows
|
79
|
+
target = target_name
|
80
|
+
|
81
|
+
if target
|
82
|
+
target_pointer = wstring(target)
|
83
|
+
info_ptr = FFI::MemoryPointer.new(:pointer)
|
84
|
+
cred = CREDENTIAL_OBJECT.new info_ptr
|
85
|
+
cred_result = CredReadW(target_pointer, CRED_TYPE_GENERIC, 0, cred)
|
86
|
+
translated_cred = CREDENTIAL_OBJECT.new(info_ptr.read_pointer)
|
87
|
+
|
88
|
+
target_obj = translated_cred[:TargetName].read_wstring.split("::") if translated_cred[:TargetName].read_wstring
|
89
|
+
cred_blob = translated_cred[:CredentialBlob].get_bytes(0, translated_cred[:CredentialBlobSize]).split("::")
|
90
|
+
|
91
|
+
tokentype = target_obj.select { |obj| obj.include? "tokenType" }
|
92
|
+
user = target_obj.select { |obj| obj.include? "userId" }
|
93
|
+
clientid = target_obj.select { |obj| obj.include? "clientId" }
|
94
|
+
expiry_time = target_obj.select { |obj| obj.include? "expiresOn" }
|
95
|
+
access_token = cred_blob.select { |obj| obj.include? "a:" }
|
96
|
+
refresh_token = cred_blob.select { |obj| obj.include? "r:" }
|
97
|
+
|
98
|
+
credential = {}
|
99
|
+
credential[:tokentype] = tokentype[0].split(":")[1] if tokentype
|
100
|
+
credential[:user] = user[0].split(":")[1] if user
|
101
|
+
credential[:token] = access_token[0].split(":")[1] if access_token
|
102
|
+
credential[:refresh_token] = refresh_token[0].split(":")[1] if refresh_token
|
103
|
+
credential[:clientid] = clientid[0].split(":")[1] if clientid
|
104
|
+
credential[:expiry_time] = expiry_time[0].split("expiresOn:")[1].gsub("\\","") if expiry_time
|
105
|
+
else
|
106
|
+
raise "TargetName Not Found"
|
107
|
+
end
|
108
|
+
credential
|
109
|
+
end
|
110
|
+
|
111
|
+
def target_name
|
112
|
+
# cmdkey command is used for accessing windows credential manager
|
113
|
+
xplat_creds_cmd = Mixlib::ShellOut.new("cmdkey /list | findstr AzureXplatCli")
|
114
|
+
result = xplat_creds_cmd.run_command
|
115
|
+
|
116
|
+
target_name = ""
|
117
|
+
if result.stdout.empty?
|
118
|
+
raise "Azure Credentials not found. Please run xplat's 'azure login' command"
|
119
|
+
else
|
120
|
+
result.stdout.split("\n").each do |target|
|
121
|
+
# Three credentials get created in windows credential manager for xplat-cli
|
122
|
+
# One of them is for common tanent id, which can't be used
|
123
|
+
# Two of them end with --0-2 and --1-2. The one ending with --1-2 doesn't have
|
124
|
+
# accessToken and refreshToken in the credentialBlob.
|
125
|
+
# Selecting the one ending with --0-2
|
126
|
+
if !target.include?("common::") && target.include?("--0-2")
|
127
|
+
target_name = target.gsub("Target:","").strip
|
128
|
+
break
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
target_name
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
#
|
2
|
+
# Author::
|
3
|
+
# Copyright:: Copyright (c) 2016 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
|
+
require 'azure/azure_interface'
|
20
|
+
require 'azure/service_management/rest'
|
21
|
+
require 'azure/service_management/connection'
|
22
|
+
|
23
|
+
module Azure
|
24
|
+
class ServiceManagement
|
25
|
+
class ASMInterface < AzureInterface
|
26
|
+
include AzureAPI
|
27
|
+
|
28
|
+
attr_accessor :connection
|
29
|
+
|
30
|
+
def initialize(params = {})
|
31
|
+
@rest = Rest.new(params)
|
32
|
+
@connection = Azure::ServiceManagement::Connection.new(@rest)
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def list_images
|
37
|
+
connection.images.all
|
38
|
+
end
|
39
|
+
|
40
|
+
def list_servers
|
41
|
+
servers = connection.roles.all
|
42
|
+
cols = ['DNS Name', 'VM Name', 'Status', 'IP Address', 'SSH Port', 'WinRM Port' ]
|
43
|
+
rows = []
|
44
|
+
servers.each do |server|
|
45
|
+
rows << server.hostedservicename.to_s+".cloudapp.net" # Info about the DNS name at http://msdn.microsoft.com/en-us/library/ee460806.aspx
|
46
|
+
rows << server.name.to_s
|
47
|
+
rows << begin
|
48
|
+
state = server.status.to_s.downcase
|
49
|
+
case state
|
50
|
+
when 'shutting-down','terminated','stopping','stopped'
|
51
|
+
ui.color(state, :red)
|
52
|
+
when 'pending'
|
53
|
+
ui.color(state, :yellow)
|
54
|
+
else
|
55
|
+
ui.color('ready', :green)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rows << server.publicipaddress.to_s
|
59
|
+
rows << server.sshport.to_s
|
60
|
+
rows << server.winrmport.to_s
|
61
|
+
end
|
62
|
+
display_list(ui, cols, rows)
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_server(params = {})
|
66
|
+
server = connection.roles.find(params[:name], params = { :azure_dns_name => params[:azure_dns_name] })
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete_server(params = {})
|
70
|
+
server = find_server({name: params[:name], azure_dns_name: params[:azure_dns_name]})
|
71
|
+
|
72
|
+
unless server
|
73
|
+
puts "\n"
|
74
|
+
ui.error("Server #{params[:name]} does not exist")
|
75
|
+
exit!
|
76
|
+
end
|
77
|
+
|
78
|
+
puts "\n"
|
79
|
+
msg_pair(ui, 'DNS Name', server.hostedservicename + ".cloudapp.net")
|
80
|
+
msg_pair(ui, 'VM Name', server.name)
|
81
|
+
msg_pair(ui, 'Size', server.size)
|
82
|
+
msg_pair(ui, 'Public Ip Address', server.publicipaddress)
|
83
|
+
puts "\n"
|
84
|
+
|
85
|
+
begin
|
86
|
+
ui.confirm('Do you really want to delete this server')
|
87
|
+
rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception
|
88
|
+
server = nil # Cleanup is implicitly performed in other cloud plugins
|
89
|
+
exit!
|
90
|
+
end
|
91
|
+
|
92
|
+
params[:azure_dns_name] = server.hostedservicename
|
93
|
+
|
94
|
+
connection.roles.delete(params)
|
95
|
+
|
96
|
+
puts "\n"
|
97
|
+
ui.warn("Deleted server #{server.name}")
|
98
|
+
end
|
99
|
+
|
100
|
+
def show_server(name)
|
101
|
+
begin
|
102
|
+
role = connection.roles.find name
|
103
|
+
|
104
|
+
puts ''
|
105
|
+
if (role)
|
106
|
+
details = Array.new
|
107
|
+
details << ui.color('Role name', :bold, :cyan)
|
108
|
+
details << role.name
|
109
|
+
details << ui.color('Status', :bold, :cyan)
|
110
|
+
details << role.status
|
111
|
+
details << ui.color('Size', :bold, :cyan)
|
112
|
+
details << role.size
|
113
|
+
details << ui.color('Hosted service name', :bold, :cyan)
|
114
|
+
details << role.hostedservicename
|
115
|
+
details << ui.color('Deployment name', :bold, :cyan)
|
116
|
+
details << role.deployname
|
117
|
+
details << ui.color('Host name', :bold, :cyan)
|
118
|
+
details << role.hostname
|
119
|
+
unless role.sshport.nil?
|
120
|
+
details << ui.color('SSH port', :bold, :cyan)
|
121
|
+
details << role.sshport
|
122
|
+
end
|
123
|
+
unless role.winrmport.nil?
|
124
|
+
details << ui.color('WinRM port', :bold, :cyan)
|
125
|
+
details << role.winrmport
|
126
|
+
end
|
127
|
+
details << ui.color('Public IP', :bold, :cyan)
|
128
|
+
details << role.publicipaddress.to_s
|
129
|
+
|
130
|
+
unless role.thumbprint.empty?
|
131
|
+
details << ui.color('Thumbprint', :bold, :cyan)
|
132
|
+
details << role.thumbprint
|
133
|
+
end
|
134
|
+
puts ui.list(details, :columns_across, 2)
|
135
|
+
if role.tcpports.length > 0 || role.udpports.length > 0
|
136
|
+
details.clear
|
137
|
+
details << ui.color('Ports open', :bold, :cyan)
|
138
|
+
details << ui.color('Local port', :bold, :cyan)
|
139
|
+
details << ui.color('IP', :bold, :cyan)
|
140
|
+
details << ui.color('Public port', :bold, :cyan)
|
141
|
+
if role.tcpports.length > 0
|
142
|
+
role.tcpports.each do |port|
|
143
|
+
details << 'tcp'
|
144
|
+
details << port['LocalPort']
|
145
|
+
details << port['Vip']
|
146
|
+
details << port['PublicPort']
|
147
|
+
end
|
148
|
+
end
|
149
|
+
if role.udpports.length > 0
|
150
|
+
role.udpports.each do |port|
|
151
|
+
details << 'udp'
|
152
|
+
details << port['LocalPort']
|
153
|
+
details << port['Vip']
|
154
|
+
details << port['PublicPort']
|
155
|
+
end
|
156
|
+
end
|
157
|
+
puts ui.list(details, :columns_across, 4)
|
158
|
+
end
|
159
|
+
else
|
160
|
+
puts "No VM found"
|
161
|
+
end
|
162
|
+
|
163
|
+
rescue => error
|
164
|
+
puts "#{error.class} and #{error.message}"
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def list_internal_lb
|
170
|
+
lbs = connection.lbs.all
|
171
|
+
cols = %w{Name Service Subnet VIP}
|
172
|
+
rows = []
|
173
|
+
lbs.each do |lb|
|
174
|
+
cols.each { |col| rows << lb.send(col.downcase).to_s }
|
175
|
+
end
|
176
|
+
display_list(ui, cols, rows)
|
177
|
+
end
|
178
|
+
|
179
|
+
def create_internal_lb(params = {})
|
180
|
+
connection.lbs.create(params)
|
181
|
+
end
|
182
|
+
|
183
|
+
def list_vnets
|
184
|
+
vnets = connection.vnets.all
|
185
|
+
cols = ['Name', 'Affinity Group', 'State']
|
186
|
+
rows = []
|
187
|
+
vnets.each do |vnet|
|
188
|
+
%w(name affinity_group state).each{ |col| rows << vnet.send(col).to_s }
|
189
|
+
end
|
190
|
+
display_list(ui, cols, rows)
|
191
|
+
end
|
192
|
+
|
193
|
+
def create_vnet(params = {})
|
194
|
+
connection.vnets.create(params)
|
195
|
+
end
|
196
|
+
|
197
|
+
def list_affinity_groups
|
198
|
+
affinity_groups = connection.ags.all
|
199
|
+
cols = %w{Name Location Description}
|
200
|
+
rows = []
|
201
|
+
affinity_groups.each do |affinity_group|
|
202
|
+
cols.each { |col| rows << affinity_group.send(col.downcase).to_s }
|
203
|
+
end
|
204
|
+
display_list(ui, cols, rows)
|
205
|
+
end
|
206
|
+
|
207
|
+
def create_affinity_group(params = {})
|
208
|
+
connection.ags.create(params)
|
209
|
+
end
|
210
|
+
|
211
|
+
def create_server(params = {})
|
212
|
+
remove_hosted_service_on_failure = params[:azure_dns_name]
|
213
|
+
if connection.hosts.exists?(params[:azure_dns_name])
|
214
|
+
remove_hosted_service_on_failure = nil
|
215
|
+
end
|
216
|
+
|
217
|
+
#If Storage Account is not specified, check if the geographic location has one to re-use
|
218
|
+
if not params[:azure_storage_account]
|
219
|
+
storage_accts = connection.storageaccounts.all
|
220
|
+
storage = storage_accts.find { |storage_acct| storage_acct.location.to_s == params[:azure_service_location] }
|
221
|
+
unless storage
|
222
|
+
params[:azure_storage_account] = [strip_non_ascii(params[:azure_vm_name]), random_string].join.downcase
|
223
|
+
remove_storage_service_on_failure = params[:azure_storage_account]
|
224
|
+
else
|
225
|
+
remove_storage_service_on_failure = nil
|
226
|
+
params[:azure_storage_account] = storage.name.to_s
|
227
|
+
end
|
228
|
+
else
|
229
|
+
if connection.storageaccounts.exists?(params[:azure_storage_account])
|
230
|
+
remove_storage_service_on_failure = nil
|
231
|
+
else
|
232
|
+
remove_storage_service_on_failure = params[:azure_storage_account]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
begin
|
237
|
+
connection.deploys.create(params)
|
238
|
+
rescue Exception => e
|
239
|
+
Chef::Log.error("Failed to create the server -- exception being rescued: #{e.to_s}")
|
240
|
+
backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
241
|
+
Chef::Log.debug("#{backtrace_message}")
|
242
|
+
cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
def cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
|
248
|
+
Chef::Log.warn("Cleaning up resources...")
|
249
|
+
|
250
|
+
if remove_hosted_service_on_failure
|
251
|
+
ret_val = connection.hosts.delete(remove_hosted_service_on_failure)
|
252
|
+
ret_val.content.empty? ? Chef::Log.warn("Deleted created DNS: #{remove_hosted_service_on_failure}.") : Chef::Log.warn("Deletion failed for created DNS:#{remove_hosted_service_on_failure}. " + ret_val.text)
|
253
|
+
end
|
254
|
+
|
255
|
+
if remove_storage_service_on_failure
|
256
|
+
ret_val = connection.storageaccounts.delete(remove_storage_service_on_failure)
|
257
|
+
ret_val.content.empty? ? Chef::Log.warn("Deleted created Storage Account: #{remove_storage_service_on_failure}.") : Chef::Log.warn("Deletion failed for created Storage Account: #{remove_storage_service_on_failure}. " + ret_val.text)
|
258
|
+
end
|
259
|
+
exit 1
|
260
|
+
end
|
261
|
+
|
262
|
+
def get_role_server(dns_name, vm_name)
|
263
|
+
deploy = connection.deploys.queryDeploy(dns_name)
|
264
|
+
deploy.find_role(vm_name)
|
265
|
+
end
|
266
|
+
|
267
|
+
def get_extension(name, publisher)
|
268
|
+
connection.query_azure("resourceextensions/#{publisher}/#{name}")
|
269
|
+
end
|
270
|
+
|
271
|
+
def deployment_name(dns_name)
|
272
|
+
connection.deploys.get_deploy_name_for_hostedservice(dns_name)
|
273
|
+
end
|
274
|
+
|
275
|
+
def deployment(path)
|
276
|
+
connection.query_azure(path)
|
277
|
+
end
|
278
|
+
|
279
|
+
def valid_image?(name)
|
280
|
+
connection.images.exists?(name)
|
281
|
+
end
|
282
|
+
|
283
|
+
def vm_image?(name)
|
284
|
+
connection.images.is_vm_image(name)
|
285
|
+
end
|
286
|
+
|
287
|
+
def add_extension(name, params = {})
|
288
|
+
begin
|
289
|
+
ui.info "Started with Chef Extension deployment on the server #{name}..."
|
290
|
+
connection.roles.update(name, params)
|
291
|
+
ui.info "\nSuccessfully deployed Chef Extension on the server #{name}."
|
292
|
+
rescue Exception => e
|
293
|
+
Chef::Log.error("Failed to add extension to the server -- exception being rescued: #{e.to_s}")
|
294
|
+
backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
295
|
+
Chef::Log.debug("#{backtrace_message}")
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
@@ -16,7 +16,7 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
-
|
19
|
+
module Azure
|
20
20
|
class AGs
|
21
21
|
def initialize(connection)
|
22
22
|
@connection = connection
|
@@ -58,7 +58,7 @@ class Azure
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
|
61
|
+
module Azure
|
62
62
|
class AG
|
63
63
|
attr_accessor :name, :label, :description, :location
|
64
64
|
|