knife-azure 3.0.6 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|