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.
- 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
|
|