knife-azure 3.0.6 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/azure/custom_errors.rb +1 -1
- data/lib/azure/resource_management/ARM_deployment_template.rb +5 -5
- data/lib/azure/resource_management/ARM_interface.rb +4 -6
- data/lib/azure/resource_management/windows_credentials.rb +2 -2
- data/lib/chef/knife/azurerm_server_create.rb +1 -1
- data/lib/chef/knife/bootstrap/bootstrapper.rb +5 -10
- data/lib/chef/knife/helpers/azurerm_base.rb +4 -4
- data/lib/knife-azure/version.rb +1 -1
- metadata +30 -43
- data/lib/azure/service_management/ASM_interface.rb +0 -310
- data/lib/azure/service_management/ag.rb +0 -99
- data/lib/azure/service_management/certificate.rb +0 -235
- data/lib/azure/service_management/connection.rb +0 -102
- data/lib/azure/service_management/deploy.rb +0 -221
- data/lib/azure/service_management/disk.rb +0 -68
- data/lib/azure/service_management/host.rb +0 -184
- data/lib/azure/service_management/image.rb +0 -94
- data/lib/azure/service_management/loadbalancer.rb +0 -78
- data/lib/azure/service_management/rest.rb +0 -126
- data/lib/azure/service_management/role.rb +0 -717
- data/lib/azure/service_management/storageaccount.rb +0 -127
- data/lib/azure/service_management/utility.rb +0 -40
- data/lib/azure/service_management/vnet.rb +0 -134
- data/lib/chef/knife/azure_ag_create.rb +0 -73
- data/lib/chef/knife/azure_ag_list.rb +0 -35
- data/lib/chef/knife/azure_image_list.rb +0 -56
- data/lib/chef/knife/azure_internal-lb_create.rb +0 -74
- data/lib/chef/knife/azure_internal-lb_list.rb +0 -35
- data/lib/chef/knife/azure_server_create.rb +0 -531
- data/lib/chef/knife/azure_server_delete.rb +0 -136
- data/lib/chef/knife/azure_server_list.rb +0 -38
- data/lib/chef/knife/azure_server_show.rb +0 -41
- data/lib/chef/knife/azure_vnet_create.rb +0 -74
- data/lib/chef/knife/azure_vnet_list.rb +0 -35
- data/lib/chef/knife/bootstrap_azure.rb +0 -191
- data/lib/chef/knife/helpers/azure_base.rb +0 -392
@@ -1,99 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Jeff Mendoza (jeffmendoza@live.com)
|
3
|
-
# Copyright:: Copyright (c) Chef Software Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
module Azure
|
20
|
-
class AGs
|
21
|
-
def initialize(connection)
|
22
|
-
@connection = connection
|
23
|
-
end
|
24
|
-
|
25
|
-
def load
|
26
|
-
@ags ||= begin
|
27
|
-
@ags = {}
|
28
|
-
response = @connection.query_azure("affinitygroups",
|
29
|
-
"get",
|
30
|
-
"",
|
31
|
-
"",
|
32
|
-
true,
|
33
|
-
false)
|
34
|
-
response.css("AffinityGroup").each do |ag|
|
35
|
-
item = AG.new(@connection).parse(ag)
|
36
|
-
@ags[item.name] = item
|
37
|
-
end
|
38
|
-
@ags
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def all
|
43
|
-
load.values
|
44
|
-
end
|
45
|
-
|
46
|
-
def exists?(name)
|
47
|
-
load.key?(name)
|
48
|
-
end
|
49
|
-
|
50
|
-
def find(name)
|
51
|
-
load[name]
|
52
|
-
end
|
53
|
-
|
54
|
-
def create(params)
|
55
|
-
ag = AG.new(@connection)
|
56
|
-
ag.create(params)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
module Azure
|
62
|
-
class AG
|
63
|
-
attr_accessor :name, :label, :description, :location
|
64
|
-
|
65
|
-
def initialize(connection)
|
66
|
-
@connection = connection
|
67
|
-
end
|
68
|
-
|
69
|
-
def parse(image)
|
70
|
-
@name = image.at_css("Name").content
|
71
|
-
@label = image.at_css("Label").content
|
72
|
-
@description = image.at_css("Description").content if
|
73
|
-
image.at_css("Description")
|
74
|
-
@location = image.at_css("Location").content if image.at_css("Location")
|
75
|
-
self
|
76
|
-
end
|
77
|
-
|
78
|
-
def create(params)
|
79
|
-
builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
80
|
-
xml.CreateAffinityGroup(
|
81
|
-
xmlns: "http://schemas.microsoft.com/windowsazure"
|
82
|
-
) do
|
83
|
-
xml.Name params[:azure_ag_name]
|
84
|
-
xml.Label Base64.strict_encode64(params[:azure_ag_name])
|
85
|
-
unless params[:azure_ag_desc].nil?
|
86
|
-
xml.Description params[:azure_ag_desc]
|
87
|
-
end
|
88
|
-
xml.Location params[:azure_location]
|
89
|
-
end
|
90
|
-
end
|
91
|
-
@connection.query_azure("affinitygroups",
|
92
|
-
"post",
|
93
|
-
builder.to_xml,
|
94
|
-
"",
|
95
|
-
true,
|
96
|
-
false)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,235 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Mukta Aphale (mukta.aphale@clogeny.com)
|
3
|
-
# Copyright:: Copyright (c) Chef Software Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
module Azure
|
20
|
-
class Certificates
|
21
|
-
def initialize(connection)
|
22
|
-
@connection = connection
|
23
|
-
end
|
24
|
-
|
25
|
-
def create(params)
|
26
|
-
certificate = Certificate.new(@connection)
|
27
|
-
certificate.create(params)
|
28
|
-
end
|
29
|
-
|
30
|
-
def add(certificate_data, certificate_password, certificate_format, dns_name)
|
31
|
-
certificate = Certificate.new(@connection)
|
32
|
-
certificate.add_certificate certificate_data, certificate_password, certificate_format, dns_name
|
33
|
-
end
|
34
|
-
|
35
|
-
def create_ssl_certificate(azure_dns_name)
|
36
|
-
cert_params = { output_file: "winrm", key_length: 2048, cert_validity: 24,
|
37
|
-
azure_dns_name: azure_dns_name }
|
38
|
-
certificate = Certificate.new(@connection)
|
39
|
-
thumbprint = certificate.create_ssl_certificate(cert_params)
|
40
|
-
end
|
41
|
-
|
42
|
-
def get_certificate(dns_name, fingerprint)
|
43
|
-
certificate = Certificate.new(@connection)
|
44
|
-
certificate.get_certificate(dns_name, fingerprint)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
module Azure
|
50
|
-
class Certificate
|
51
|
-
attr_accessor :connection
|
52
|
-
attr_accessor :cert_data, :fingerprint, :certificate_version
|
53
|
-
def initialize(connection)
|
54
|
-
@connection = connection
|
55
|
-
@certificate_version = 2 # cf. RFC 5280 - to make it a "v3" certificate
|
56
|
-
end
|
57
|
-
|
58
|
-
def create(params)
|
59
|
-
# If RSA private key has been specified, then generate an x 509 certificate from the
|
60
|
-
# public part of the key
|
61
|
-
@cert_data = generate_public_key_certificate_data({ ssh_key: params[:ssh_identity_file],
|
62
|
-
ssh_key_passphrase: params[:identity_file_passphrase] })
|
63
|
-
add_certificate @cert_data, "knifeazure", "pfx", params[:azure_dns_name]
|
64
|
-
|
65
|
-
# Return the fingerprint to be used while adding role
|
66
|
-
@fingerprint
|
67
|
-
end
|
68
|
-
|
69
|
-
def generate_public_key_certificate_data(params)
|
70
|
-
# Generate OpenSSL RSA key from the mentioned ssh key path (and passphrase)
|
71
|
-
key = OpenSSL::PKey::RSA.new(File.read(params[:ssh_key]), params[:ssh_key_passphrase])
|
72
|
-
# Generate X 509 certificate
|
73
|
-
ca = OpenSSL::X509::Certificate.new
|
74
|
-
ca.version = @certificate_version
|
75
|
-
ca.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
|
76
|
-
ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=knife-plugin/CN=Opscode CA"
|
77
|
-
ca.issuer = ca.subject # root CA's are "self-signed"
|
78
|
-
ca.public_key = key.public_key # Assign the ssh-key's public part to the certificate
|
79
|
-
ca.not_before = Time.now
|
80
|
-
ca.not_after = ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
|
81
|
-
ef = OpenSSL::X509::ExtensionFactory.new
|
82
|
-
ef.subject_certificate = ca
|
83
|
-
ef.issuer_certificate = ca
|
84
|
-
ca.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
|
85
|
-
ca.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
|
86
|
-
ca.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
|
87
|
-
ca.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
|
88
|
-
ca.sign(key, OpenSSL::Digest.new("SHA256"))
|
89
|
-
# Generate the SHA1 fingerprint of the der format of the X 509 certificate
|
90
|
-
@fingerprint = OpenSSL::Digest::SHA1.new(ca.to_der)
|
91
|
-
# Create the pfx format of the certificate
|
92
|
-
pfx = OpenSSL::PKCS12.create("knifeazure", "knife-azure-pfx", key, ca)
|
93
|
-
# Encode the pfx format - upload this certificate
|
94
|
-
Base64.strict_encode64(pfx.to_der)
|
95
|
-
end
|
96
|
-
|
97
|
-
def add_certificate(certificate_data, certificate_password, certificate_format, dns_name)
|
98
|
-
# Generate XML to call the API
|
99
|
-
# Add certificate to the hosted service
|
100
|
-
builder = Nokogiri::XML::Builder.new do |xml|
|
101
|
-
xml.CertificateFile("xmlns" => "http://schemas.microsoft.com/windowsazure") do
|
102
|
-
xml.Data certificate_data
|
103
|
-
xml.CertificateFormat certificate_format
|
104
|
-
xml.Password certificate_password
|
105
|
-
end
|
106
|
-
end
|
107
|
-
# Windows Azure API call
|
108
|
-
@connection.query_azure("hostedservices/#{dns_name}/certificates", "post", builder.to_xml)
|
109
|
-
|
110
|
-
# Check if certificate is available else raise error
|
111
|
-
for attempt in 0..4
|
112
|
-
Chef::Log.info "Waiting to get certificate ..."
|
113
|
-
res = get_certificate(dns_name, @fingerprint)
|
114
|
-
break unless res.empty?
|
115
|
-
if attempt == 4
|
116
|
-
raise "The certificate with thumbprint #{fingerprint} was not found."
|
117
|
-
else
|
118
|
-
sleep 5
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def get_certificate(dns_name, fingerprint)
|
124
|
-
@connection.query_azure("hostedservices/#{dns_name}/certificates/sha1-#{fingerprint}", "get").search("Certificate")
|
125
|
-
end
|
126
|
-
|
127
|
-
######## SSL certificate generation for knife-azure ssl bootstrap ######
|
128
|
-
def create_ssl_certificate(cert_params)
|
129
|
-
file_path = cert_params[:output_file].sub(/\.(\w+)$/, "")
|
130
|
-
path = prompt_for_file_path
|
131
|
-
file_path = File.join(path, file_path) unless path.empty?
|
132
|
-
cert_params[:domain] = prompt_for_domain
|
133
|
-
|
134
|
-
rsa_key = generate_keypair cert_params[:key_length]
|
135
|
-
cert = generate_certificate(rsa_key, cert_params)
|
136
|
-
write_certificate_to_file cert, file_path, rsa_key, cert_params
|
137
|
-
puts "*" * 70
|
138
|
-
puts "Generated Certificates:"
|
139
|
-
puts "- #{file_path}.pfx - PKCS12 format keypair. Contains both the public and private keys, usually used on the server."
|
140
|
-
puts "- #{file_path}.b64 - Base64 encoded PKCS12 keypair. Contains both the public and private keys, for upload to the Azure REST API."
|
141
|
-
puts "- #{file_path}.pem - Base64 encoded public certificate only. Required by the client to connect to the server."
|
142
|
-
puts "Certificate Thumbprint: #{@thumbprint.to_s.upcase}"
|
143
|
-
puts "*" * 70
|
144
|
-
|
145
|
-
config[:ca_trust_file] = file_path + ".pem" if config[:ca_trust_file].nil?
|
146
|
-
cert_data = File.read (file_path + ".b64")
|
147
|
-
add_certificate cert_data, @winrm_cert_passphrase, "pfx", cert_params[:azure_dns_name]
|
148
|
-
@thumbprint
|
149
|
-
end
|
150
|
-
|
151
|
-
def generate_keypair(key_length)
|
152
|
-
OpenSSL::PKey::RSA.new(key_length.to_i)
|
153
|
-
end
|
154
|
-
|
155
|
-
def prompt_for_passphrase
|
156
|
-
passphrase = ""
|
157
|
-
begin
|
158
|
-
print "Passphrases do not match. Try again.\n" unless passphrase.empty?
|
159
|
-
print "Enter certificate passphrase (empty for no passphrase):"
|
160
|
-
passphrase = STDIN.gets
|
161
|
-
return passphrase.strip if passphrase == "\n"
|
162
|
-
|
163
|
-
print "Enter same passphrase again:"
|
164
|
-
confirm_passphrase = STDIN.gets
|
165
|
-
end until passphrase == confirm_passphrase
|
166
|
-
passphrase.strip
|
167
|
-
end
|
168
|
-
|
169
|
-
def prompt_for_file_path
|
170
|
-
file_path = ""
|
171
|
-
counter = 0
|
172
|
-
begin
|
173
|
-
print "Invalid location! \n" unless file_path.empty?
|
174
|
-
print 'Enter the file path for certificates e.g. C:\Windows (empty for current location):'
|
175
|
-
file_path = STDIN.gets
|
176
|
-
stripped_file_path = file_path.strip
|
177
|
-
return stripped_file_path if file_path == "\n"
|
178
|
-
|
179
|
-
counter += 1
|
180
|
-
exit(1) if counter == 3
|
181
|
-
end until File.directory?(stripped_file_path)
|
182
|
-
stripped_file_path
|
183
|
-
end
|
184
|
-
|
185
|
-
def prompt_for_domain
|
186
|
-
counter = 0
|
187
|
-
begin
|
188
|
-
print "Enter the domain (mandatory):"
|
189
|
-
domain = STDIN.gets
|
190
|
-
domain = domain.strip
|
191
|
-
counter += 1
|
192
|
-
exit(1) if counter == 3
|
193
|
-
end until !domain.empty?
|
194
|
-
domain
|
195
|
-
end
|
196
|
-
|
197
|
-
def generate_certificate(rsa_key, cert_params)
|
198
|
-
@hostname = "*"
|
199
|
-
if cert_params[:domain]
|
200
|
-
@hostname = "*." + cert_params[:domain]
|
201
|
-
end
|
202
|
-
|
203
|
-
# Create a self-signed X509 certificate from the rsa_key (unencrypted)
|
204
|
-
cert = OpenSSL::X509::Certificate.new
|
205
|
-
cert.version = 2
|
206
|
-
cert.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
|
207
|
-
|
208
|
-
cert.subject = OpenSSL::X509::Name.parse "/CN=#{@hostname}"
|
209
|
-
cert.issuer = cert.subject
|
210
|
-
cert.public_key = rsa_key.public_key
|
211
|
-
cert.not_before = Time.now
|
212
|
-
cert.not_after = cert.not_before + 2 * 365 * cert_params[:cert_validity].to_i * 60 * 60 # 2 years validity
|
213
|
-
ef = OpenSSL::X509::ExtensionFactory.new
|
214
|
-
ef.subject_certificate = cert
|
215
|
-
ef.issuer_certificate = cert
|
216
|
-
cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
|
217
|
-
cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
|
218
|
-
cert.add_extension(ef.create_extension("extendedKeyUsage", "1.3.6.1.5.5.7.3.1", false))
|
219
|
-
cert.sign(rsa_key, OpenSSL::Digest.new("SHA1"))
|
220
|
-
@thumbprint = OpenSSL::Digest::SHA1.new(cert.to_der)
|
221
|
-
cert
|
222
|
-
end
|
223
|
-
|
224
|
-
def write_certificate_to_file(cert, file_path, rsa_key, cert_params)
|
225
|
-
File.open(file_path + ".pem", "wb") { |f| f.print cert.to_pem }
|
226
|
-
@winrm_cert_passphrase = prompt_for_passphrase unless @winrm_cert_passphrase
|
227
|
-
pfx = OpenSSL::PKCS12.create("#{cert_params[:winrm_cert_passphrase]}", "winrmcert", rsa_key, cert)
|
228
|
-
File.open(file_path + ".pfx", "wb") { |f| f.print pfx.to_der }
|
229
|
-
File.open(file_path + ".b64", "wb") { |f| f.print Base64.strict_encode64(pfx.to_der) }
|
230
|
-
end
|
231
|
-
|
232
|
-
########## SSL certificate generation ends ###########
|
233
|
-
|
234
|
-
end
|
235
|
-
end
|
@@ -1,102 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Barry Davis (barryd@jetstreamsoftware.com)
|
3
|
-
# Copyright:: Copyright (c) Chef Software 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_relative "image"
|
20
|
-
require_relative "role"
|
21
|
-
require_relative "deploy"
|
22
|
-
require_relative "host"
|
23
|
-
require_relative "loadbalancer"
|
24
|
-
require_relative "vnet"
|
25
|
-
require_relative "utility"
|
26
|
-
require_relative "ag"
|
27
|
-
require_relative "storageaccount"
|
28
|
-
require_relative "certificate"
|
29
|
-
require_relative "disk"
|
30
|
-
|
31
|
-
module Azure
|
32
|
-
class ServiceManagement
|
33
|
-
class Connection
|
34
|
-
include AzureUtility
|
35
|
-
attr_accessor :hosts, :rest, :images, :deploys, :roles,
|
36
|
-
:disks, :storageaccounts, :certificates, :ags, :vnets, :lbs
|
37
|
-
def initialize(rest)
|
38
|
-
@images = Images.new(self)
|
39
|
-
@roles = Roles.new(self)
|
40
|
-
@deploys = Deploys.new(self)
|
41
|
-
@hosts = Hosts.new(self)
|
42
|
-
@rest = rest
|
43
|
-
@lbs = Loadbalancer.new(self)
|
44
|
-
@vnets = Vnets.new(self)
|
45
|
-
@ags = AGs.new(self)
|
46
|
-
@storageaccounts = StorageAccounts.new(self)
|
47
|
-
@certificates = Certificates.new(self)
|
48
|
-
@disks = Disks.new(self)
|
49
|
-
end
|
50
|
-
|
51
|
-
def query_azure(service_name,
|
52
|
-
verb = "get",
|
53
|
-
body = "",
|
54
|
-
params = "",
|
55
|
-
wait = true,
|
56
|
-
services = true,
|
57
|
-
content_type = nil)
|
58
|
-
Chef::Log.info "calling " + verb + " " + service_name + (wait ? " synchronously" : " asynchronously")
|
59
|
-
Chef::Log.debug body unless body == ""
|
60
|
-
response = @rest.query_azure(service_name, verb, body, params, services, content_type)
|
61
|
-
if response.code.to_i == 200
|
62
|
-
ret_val = Nokogiri::XML response.body
|
63
|
-
elsif !wait && response.code.to_i == 202
|
64
|
-
Chef::Log.debug "Request accepted in asynchronous mode"
|
65
|
-
ret_val = Nokogiri::XML response.body
|
66
|
-
elsif response.code.to_i >= 201 && response.code.to_i <= 299
|
67
|
-
ret_val = wait_for_completion
|
68
|
-
else
|
69
|
-
if response.body
|
70
|
-
ret_val = Nokogiri::XML response.body
|
71
|
-
Chef::Log.debug ret_val.to_xml
|
72
|
-
error_code, error_message = error_from_response_xml(ret_val)
|
73
|
-
Chef::Log.debug error_code + " : " + error_message if error_code.length > 0
|
74
|
-
else
|
75
|
-
Chef::Log.warn "http error: " + response.code
|
76
|
-
end
|
77
|
-
end
|
78
|
-
ret_val
|
79
|
-
end
|
80
|
-
|
81
|
-
def wait_for_completion
|
82
|
-
status = "InProgress"
|
83
|
-
Chef::Log.info "Waiting while status returns InProgress"
|
84
|
-
while status == "InProgress"
|
85
|
-
response = @rest.query_for_completion
|
86
|
-
ret_val = Nokogiri::XML response.body
|
87
|
-
status = xml_content(ret_val, "Status")
|
88
|
-
if status == "InProgress"
|
89
|
-
print "."
|
90
|
-
sleep(0.5)
|
91
|
-
elsif status == "Succeeded"
|
92
|
-
Chef::Log.debug "not InProgress : " + ret_val.to_xml
|
93
|
-
else
|
94
|
-
error_code, error_message = error_from_response_xml(ret_val)
|
95
|
-
Chef::Log.debug status + error_code + " : " + error_message if error_code.length > 0
|
96
|
-
end
|
97
|
-
end
|
98
|
-
ret_val
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,221 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Barry Davis (barryd@jetstreamsoftware.com)
|
3
|
-
# Copyright:: Copyright (c) Chef Software Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
module Azure
|
20
|
-
class Deploys
|
21
|
-
include AzureUtility
|
22
|
-
def initialize(connection)
|
23
|
-
@connection = connection
|
24
|
-
end
|
25
|
-
|
26
|
-
# force_load should be true when there is something in local cache and we want to reload
|
27
|
-
# first call is always load.
|
28
|
-
def load(force_load = false)
|
29
|
-
unless @deploys || force_load
|
30
|
-
@deploys = begin
|
31
|
-
deploys = []
|
32
|
-
hosts = @connection.hosts.all
|
33
|
-
hosts.each do |host|
|
34
|
-
deploy = Deploy.new(@connection)
|
35
|
-
deploy.retrieve(host.name)
|
36
|
-
if deploy.name
|
37
|
-
host.add_deploy(deploy)
|
38
|
-
deploys << deploy
|
39
|
-
end
|
40
|
-
end
|
41
|
-
deploys
|
42
|
-
end
|
43
|
-
end
|
44
|
-
@deploys
|
45
|
-
end
|
46
|
-
|
47
|
-
def all
|
48
|
-
load
|
49
|
-
end
|
50
|
-
|
51
|
-
# TODO - Current knife-azure plug-in seems to have assumption that single hostedservice
|
52
|
-
# will always have one deployment (production). see Deploy#retrieve below
|
53
|
-
def get_deploy_name_for_hostedservice(hostedservicename)
|
54
|
-
host = @connection.hosts.find(hostedservicename)
|
55
|
-
if host && host.deploys.length > 0
|
56
|
-
host.deploys[0].name
|
57
|
-
else
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def create(params)
|
63
|
-
if params[:azure_connect_to_existing_dns]
|
64
|
-
unless @connection.hosts.exists?(params[:azure_dns_name])
|
65
|
-
Chef::Log.fatal "The specified Azure DNS Name does not exist."
|
66
|
-
exit 1
|
67
|
-
end
|
68
|
-
else
|
69
|
-
ret_val = @connection.hosts.create(params)
|
70
|
-
error_code, error_message = error_from_response_xml(ret_val)
|
71
|
-
if error_code.length > 0
|
72
|
-
Chef::Log.fatal "Unable to create DNS:" + error_code + " : " + error_message
|
73
|
-
exit 1
|
74
|
-
end
|
75
|
-
end
|
76
|
-
unless @connection.storageaccounts.exists?(params[:azure_storage_account])
|
77
|
-
@connection.storageaccounts.create(params)
|
78
|
-
end
|
79
|
-
if params[:ssh_identity_file]
|
80
|
-
params[:fingerprint] = @connection.certificates.create(params)
|
81
|
-
end
|
82
|
-
if params[:cert_path]
|
83
|
-
cert_data = File.read (params[:cert_path])
|
84
|
-
@connection.certificates.add cert_data, params[:cert_password], "pfx", params[:azure_dns_name]
|
85
|
-
elsif params[:winrm_ssl]
|
86
|
-
# TODO: generate certificates for ssl listener
|
87
|
-
end
|
88
|
-
|
89
|
-
params["deploy_name"] = get_deploy_name_for_hostedservice(params[:azure_dns_name])
|
90
|
-
|
91
|
-
if !params["deploy_name"].nil?
|
92
|
-
role = Role.new(@connection)
|
93
|
-
roleXML = role.setup(params)
|
94
|
-
ret_val = role.create(params, roleXML)
|
95
|
-
else
|
96
|
-
params["deploy_name"] = params[:azure_dns_name]
|
97
|
-
deploy = Deploy.new(@connection)
|
98
|
-
deployXML = deploy.setup(params)
|
99
|
-
ret_val = deploy.create(params, deployXML)
|
100
|
-
end
|
101
|
-
error_code, error_message = error_from_response_xml(ret_val)
|
102
|
-
if error_code.length > 0
|
103
|
-
Chef::Log.debug(ret_val.to_s)
|
104
|
-
raise Chef::Log.fatal "Unable to create role:" + error_code + " : " + error_message
|
105
|
-
end
|
106
|
-
@connection.roles.find_in_hosted_service(params[:azure_vm_name], params[:azure_dns_name])
|
107
|
-
end
|
108
|
-
|
109
|
-
def delete(rolename); end
|
110
|
-
|
111
|
-
def queryDeploy(hostedservicename)
|
112
|
-
deploy = Deploy.new(@connection)
|
113
|
-
deploy.retrieve(hostedservicename)
|
114
|
-
deploy
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
class Deploy
|
119
|
-
include AzureUtility
|
120
|
-
attr_accessor :connection, :name, :status, :url, :hostedservicename, :input_endpoints, :loadbalancers
|
121
|
-
|
122
|
-
def initialize(connection)
|
123
|
-
@connection = connection
|
124
|
-
end
|
125
|
-
|
126
|
-
def retrieve(hostedservicename)
|
127
|
-
@hostedservicename = hostedservicename
|
128
|
-
deployXML = @connection.query_azure("hostedservices/#{hostedservicename}/deploymentslots/Production")
|
129
|
-
if deployXML.at_css("Deployment Name") != nil
|
130
|
-
@name = xml_content(deployXML, "Deployment Name")
|
131
|
-
@status = xml_content(deployXML, "Deployment Status")
|
132
|
-
@url = xml_content(deployXML, "Deployment Url")
|
133
|
-
@roles = {}
|
134
|
-
rolesXML = deployXML.css("Deployment RoleInstanceList RoleInstance")
|
135
|
-
rolesListXML = deployXML.css("Deployment RoleList Role")
|
136
|
-
rolesXML.zip(rolesListXML).each do |roleXML, roleListXML|
|
137
|
-
role = Role.new(@connection)
|
138
|
-
role.parse(roleXML, hostedservicename, @name)
|
139
|
-
if role.publicipaddress.to_s.empty?
|
140
|
-
role.publicipaddress = xml_content(deployXML, "VirtualIPs VirtualIP Address")
|
141
|
-
end
|
142
|
-
role.parse_role_list_xml(roleListXML)
|
143
|
-
@roles[role.name] = role
|
144
|
-
end
|
145
|
-
@input_endpoints = []
|
146
|
-
endpointsXML = deployXML.css("InputEndpoint")
|
147
|
-
endpointsXML.each do |endpointXML|
|
148
|
-
@input_endpoints << parse_endpoint(endpointXML)
|
149
|
-
end
|
150
|
-
@loadbalancers = {}
|
151
|
-
lbsXML = deployXML.css("Deployment LoadBalancers LoadBalancer")
|
152
|
-
lbsXML.each do |lbXML|
|
153
|
-
loadbalancer = Loadbalancer.new(@connection)
|
154
|
-
loadbalancer.parse(lbXML, hostedservicename)
|
155
|
-
@loadbalancers[loadbalancer.name] = loadbalancer
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def setup(params)
|
161
|
-
role = Role.new(@connection)
|
162
|
-
roleXML = role.setup(params)
|
163
|
-
builder = Nokogiri::XML::Builder.new do |xml|
|
164
|
-
xml.Deployment(
|
165
|
-
"xmlns" => "http://schemas.microsoft.com/windowsazure",
|
166
|
-
"xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance"
|
167
|
-
) do
|
168
|
-
xml.Name params["deploy_name"]
|
169
|
-
xml.DeploymentSlot "Production"
|
170
|
-
xml.Label Base64.encode64(params["deploy_name"]).strip
|
171
|
-
xml.RoleList { xml.Role("i:type" => "PersistentVMRole") }
|
172
|
-
if params[:azure_network_name]
|
173
|
-
xml.VirtualNetworkName params[:azure_network_name]
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
builder.doc.at_css("Role") << roleXML.at_css("PersistentVMRole").children.to_s
|
178
|
-
builder.doc
|
179
|
-
end
|
180
|
-
|
181
|
-
def create(params, deployXML)
|
182
|
-
servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments"
|
183
|
-
@connection.query_azure(servicecall, "post", deployXML.to_xml)
|
184
|
-
end
|
185
|
-
|
186
|
-
# This parses endpoints from a RoleList-Role-InputEndpoint, NOT a RoleInstanceList-RoleInstance-InstanceEndpoint
|
187
|
-
# Refactor: make this an object rather than a hash..?
|
188
|
-
def parse_endpoint(inputendpoint_xml)
|
189
|
-
hash = {}
|
190
|
-
%w{LoadBalancedEndpointSetName LocalPort Name Port Protocol EnableDirectServerReturn LoadBalancerName IdleTimeoutInMinutes}.each do |key|
|
191
|
-
hash[key] = xml_content(inputendpoint_xml, key, nil)
|
192
|
-
end
|
193
|
-
# Protocol could be in there twice... If we have two, pick the second one as the first is for the probe.
|
194
|
-
if inputendpoint_xml.css("Protocol").count > 1
|
195
|
-
hash["Protocol"] = inputendpoint_xml.css("Protocol")[1].content
|
196
|
-
end
|
197
|
-
probe = inputendpoint_xml.css("LoadBalancerProbe")
|
198
|
-
if probe
|
199
|
-
hash["LoadBalancerProbe"] = {}
|
200
|
-
%w{Path Port Protocol IntervalInSeconds TimeoutInSeconds}.each do |key|
|
201
|
-
hash["LoadBalancerProbe"][key] = xml_content(probe, key, nil)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
hash
|
205
|
-
end
|
206
|
-
|
207
|
-
def roles
|
208
|
-
@roles.values if @roles
|
209
|
-
end
|
210
|
-
|
211
|
-
# just delete from local cache
|
212
|
-
def delete_role_if_present(role)
|
213
|
-
@roles.delete(role.name) if @roles
|
214
|
-
end
|
215
|
-
|
216
|
-
def find_role(name)
|
217
|
-
@roles[name] if @roles
|
218
|
-
end
|
219
|
-
|
220
|
-
end
|
221
|
-
end
|