knife-azure 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +275 -0
- data/lib/azure/certificate.rb +87 -0
- data/lib/azure/connection.rb +3 -1
- data/lib/azure/deploy.rb +72 -28
- data/lib/azure/host.rb +97 -18
- data/lib/azure/image.rb +21 -15
- data/lib/azure/rest.rb +9 -18
- data/lib/azure/role.rb +165 -48
- data/lib/azure/storageaccount.rb +39 -18
- data/lib/chef/knife/azure_base.rb +69 -9
- data/lib/chef/knife/azure_image_list.rb +14 -13
- data/lib/chef/knife/azure_server_create.rb +271 -108
- data/lib/chef/knife/azure_server_delete.rb +55 -13
- data/lib/chef/knife/azure_server_list.rb +9 -16
- data/lib/knife-azure/version.rb +1 -1
- metadata +119 -207
- data/README.rdoc +0 -252
data/README.md
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
# Knife Azure
|
2
|
+
|
3
|
+
## Description
|
4
|
+
A [knife] (http://docs.opscode.com/knife.html) plugin to create,
|
5
|
+
delete, and enumerate
|
6
|
+
[Windows Azure] (https://www.windowsazure.com)
|
7
|
+
resources to be managed by Chef.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
Be sure you are running the latest version of Chef, which can be installed
|
11
|
+
via:
|
12
|
+
|
13
|
+
gem install chef
|
14
|
+
|
15
|
+
This plugin is distributed as a Ruby Gem. To install it, run:
|
16
|
+
|
17
|
+
gem install knife-azure
|
18
|
+
|
19
|
+
Depending on your system's configuration, you may need to run this command
|
20
|
+
with root/administrator privileges.
|
21
|
+
|
22
|
+
## Configuration
|
23
|
+
For this plugin to interact with Azure's REST API, you will need to give Knife
|
24
|
+
information about your Azure account and credentials. The easiest way to do
|
25
|
+
this is to sign in to the Azure portal and download a publishsettings file
|
26
|
+
from https://manage.windowsazure.com/publishsettings/index?client=xplat to a
|
27
|
+
local file system location, and
|
28
|
+
then refer to the local file via an entry in your knife.rb:
|
29
|
+
|
30
|
+
knife[:azure_publish_settings_file] = "~/myazure.publishsettings"
|
31
|
+
|
32
|
+
Alternatively, all subcommands for this plugin will accept an
|
33
|
+
--azure-publish-settings-file option to allow you to specify the path to that
|
34
|
+
file with each command invocation.
|
35
|
+
|
36
|
+
The plug-in also accepts authentication information specified using an
|
37
|
+
alternative set of options -- see the section on "Alternative Management
|
38
|
+
Certificate Specification" for details.
|
39
|
+
|
40
|
+
## Basic Examples
|
41
|
+
The following examples assume that you've configured the publishsettings file
|
42
|
+
location in your knife.rb:
|
43
|
+
|
44
|
+
# List images for use in creating new VM's:
|
45
|
+
$ knife azure image list
|
46
|
+
|
47
|
+
# List all VM's (including those not be managed by Chef)
|
48
|
+
$ knife azure server list
|
49
|
+
|
50
|
+
# Create and bootstrap an Ubuntu VM over ssh
|
51
|
+
$ knife azure server create -N MyNewNode --azure-vm-size Medium --I 8fcc3d_Ubuntu-12_04-amd64-30GB -m 'West US' --ssh-user myuser --identity-file ~/.ssh/myprivatekey_rsa
|
52
|
+
|
53
|
+
# Create and bootstrap a Windows VM over winrm
|
54
|
+
$ knife azure server create --azure-dns-name MyNewServerName --azure-vm-size Medium --I 8fcc3d_Win2012-amd64-30GB -m 'West US' --winrm-user myuser --winrm-password 'mypassword' --bootstrap-protocol winrm --distro 'windows-chef-client-msi'
|
55
|
+
|
56
|
+
# Delete a server and purge it from the Chef server
|
57
|
+
$ knife azure server delete MyNewNode --purge -y
|
58
|
+
|
59
|
+
Use the --help option to read more about each subcommand. Eg:
|
60
|
+
|
61
|
+
knife azure server create --help
|
62
|
+
|
63
|
+
## Detailed Usage
|
64
|
+
|
65
|
+
### Common Configuration
|
66
|
+
Most configuration options can be specified either in your knife.rb file or as command line parameters. The CLI parameters override the knife.rb parameters.
|
67
|
+
|
68
|
+
The following options are required for all subcommands:
|
69
|
+
|
70
|
+
option :azure_publish_settings_file Path to your .publishsettings file
|
71
|
+
|
72
|
+
OR
|
73
|
+
|
74
|
+
option :azure_subscription_id Your Azure subscription ID
|
75
|
+
option :azure_mgmt_cert Management certificate in PEM format
|
76
|
+
option :azure_api_host_name Your Azure API host name
|
77
|
+
|
78
|
+
### Azure Image List Subcommand
|
79
|
+
Outputs a list of all linux images that are available to use for provisioning. You should choose one of these to use for the :azure_source_image parameter to the server create command. You can use the filter option to see a detailed image list.
|
80
|
+
|
81
|
+
knife azure image list
|
82
|
+
|
83
|
+
### Azure Server Create Subcommand
|
84
|
+
This subcommand provisions a new server in Azure and then performs a Chef bootstrap. The goal of the bootstrap is to get Chef installed on the target system so it can run Chef Client with a Chef Server.
|
85
|
+
|
86
|
+
#### Windows Bootstrapping Requirements
|
87
|
+
knife-azure depends on knife-windows: https://github.com/opscode/knife-windows
|
88
|
+
to bootstrap Windows machines via winrm (Basic, NTLM and Kerberos authentication) or ssh.
|
89
|
+
|
90
|
+
The distro/template to be used for bootstrapping is: https://github.com/opscode/knife-windows/blob/master/lib/chef/knife/bootstrap/windows-chef-client-msi.erb
|
91
|
+
|
92
|
+
Windows source images should have the WinRM service enabled and the
|
93
|
+
authentication should be set accordingly (Basic, NTLM and Kerberos). Firewall rules should be added accordingly to the source images. Refer to the link to configure this:
|
94
|
+
https://github.com/opscode/knife-windows#nodes
|
95
|
+
|
96
|
+
#### Azure-specific Options
|
97
|
+
:azure_dns_name Required. The DNS prefix name that can be used to access the cloud
|
98
|
+
service which is unique within Windows Azure. If you want to add
|
99
|
+
new VM to an existing service/deployment, specify an exiting
|
100
|
+
dns-name, along with --azure-connect-to-existing-dns option. Otherwise
|
101
|
+
a new deployment is created.
|
102
|
+
:azure_service_location Required. Specifies the geographic location of the resource as the
|
103
|
+
name of a datacenter location that is valid for your subscription. Eg:
|
104
|
+
West US, East US, East Asia, Southeast Asia, North Europe, West Europe.
|
105
|
+
:azure_source_image Required. Specifies the name of the disk image to use to create
|
106
|
+
the virtual machine. Do a "knife azure image list" to see a
|
107
|
+
list of available images.
|
108
|
+
:azure_storage_account A name for the storage account that is unique within Windows Azure.
|
109
|
+
Storage account names must be between 3 and 24 characters in
|
110
|
+
length and use numbers and lower-case letters only. This name is
|
111
|
+
the DNS prefix name and can be used to access blobs, queues, and
|
112
|
+
tables in the storage account.
|
113
|
+
:azure_vm_name Specifies the name for the virtual machine. The name must
|
114
|
+
be unique within the deployment.
|
115
|
+
:azure_os_disk_name Optional. Specifies the friendly name of the disk containing
|
116
|
+
the guest OS image in the image repository.
|
117
|
+
:azure_vm_size Size of virtual machine. Default is Small.
|
118
|
+
(ExtraSmall, Small, Medium, Large, ExtraLarge)
|
119
|
+
:azure_connect_to_existing_dns Set this flag to add the new VM to an existing
|
120
|
+
deployment/service. Must give the name of the existing
|
121
|
+
DNS correctly in the --azure-dns-name option
|
122
|
+
|
123
|
+
#### Azure VM Quick Create
|
124
|
+
You can create a server with minimal configuration. On the Azure Management Portal, this corresponds to a "Quick Create - VM". Sample command for quick create (for an Ubuntu instance):
|
125
|
+
|
126
|
+
knife azure server create
|
127
|
+
--azure-publish-settings-file '/path/to/your/cert.publishsettingsfile'
|
128
|
+
--azure-dns-name 'myservice'
|
129
|
+
--azure-service-location 'West US'
|
130
|
+
--azure-source-image 'source-image-name'
|
131
|
+
--ssh-user 'jetstream'
|
132
|
+
--identity-file '~/.ssh/myazure_rsa'
|
133
|
+
|
134
|
+
Note that the --identity-file option, which enables specification of a private
|
135
|
+
key authorized to communicate securely with the created server during the
|
136
|
+
bootstrap process, will also configure the user specified by --ssh-user with
|
137
|
+
the public key that corresponds to the private key specified by
|
138
|
+
--identity-file. This configuration persists even after the create subcommand
|
139
|
+
has completed successfully, so that the key specified with --identity-file can
|
140
|
+
be used with ssh clients for subsequent access to the server as the user
|
141
|
+
specified by --ssh-user.
|
142
|
+
|
143
|
+
You can set these options from knife.rb. A typical knife.rb is
|
144
|
+
shown below:
|
145
|
+
|
146
|
+
knife[:azure_publish_settings_file] = '/path/to/your/cert.publishsettingsfile'
|
147
|
+
knife[:azure_dns_name] = 'myservice'
|
148
|
+
knife[:azure_service_location] = 'West US'
|
149
|
+
knife[:azure_source_image] = 'source-image-name'
|
150
|
+
knife[:ssh_user] = 'jetstream'
|
151
|
+
knife[:identity_file] = '~/.ssh/myazure_rsa'
|
152
|
+
|
153
|
+
#### Azure VM Advanced Create
|
154
|
+
You can set various other options in the advanced create.
|
155
|
+
Eg: If you want to set the Azure VM Name different from that of the Azure DNS Name, set the option :azure_vm_name.
|
156
|
+
Eg: If you want to specify a Storage Account Name, set the option :azure_storage_account
|
157
|
+
|
158
|
+
To connect to an existing DNS/service, you can use a command as below:
|
159
|
+
|
160
|
+
knife azure server create
|
161
|
+
--azure-subscription-id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
162
|
+
--azure-mgmt-cert '/path/to/your/mgmtCert.pem'
|
163
|
+
--azure-api-host-name 'management.core.windows.net'
|
164
|
+
--azure-connect-to-existing-dns
|
165
|
+
--azure-dns-name 'myservice'
|
166
|
+
--azure-vm-name 'myvm02'
|
167
|
+
--azure-service-location 'West US'
|
168
|
+
--azure-source-image 'source-image-name'
|
169
|
+
--ssh-user 'jetstream'
|
170
|
+
--ssh-password 'jetstream@123'
|
171
|
+
|
172
|
+
These options may also be configured from knife.rb, as in this example:
|
173
|
+
|
174
|
+
knife[:azure_subscription_id] = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
175
|
+
knife[:azure_mgmt_cert] = '/path/to/your/mgmtCert.pem'
|
176
|
+
knife[:azure_api_host_name] = 'management.core.windows.net'
|
177
|
+
knife[:azure_service_location] = 'West US'
|
178
|
+
knife[:azure_dns_name]='myservice'
|
179
|
+
knife[:azure_vm_name]='myvm02'
|
180
|
+
knife[:ssh_user]='jetstream'
|
181
|
+
knife[:identity_file]='/path/to/RSA/private/key'
|
182
|
+
knife[:azure_storage_account]='auxpreview104'
|
183
|
+
knife[:azure_os_disk_name]='disk107'
|
184
|
+
knife[:tcp_endpoints]='66'
|
185
|
+
knife[:udp_endpoints]='77,88,99'
|
186
|
+
|
187
|
+
#### Options for Bootstrapping a Windows Node in Azure
|
188
|
+
|
189
|
+
:bootstrap_protocol Default is winrm for a windows image
|
190
|
+
:winrm_password The WinRM password
|
191
|
+
:winrm_port The WinRM port, by default this is 5985
|
192
|
+
:winrm_transport The WinRM transport type. valid choices are [ssl, plaintext]
|
193
|
+
:kerberos_keytab_file The Kerberos keytab file used for authentication
|
194
|
+
:kerberos_realm The Kerberos realm used for authentication
|
195
|
+
:kerberos_service The Kerberos service used for authentication
|
196
|
+
:ca_trust_file The Certificate Authority (CA) trust file used for SSL transport
|
197
|
+
|
198
|
+
#### Azure Windows Node Create
|
199
|
+
The quick create option requires the following options for a windows instance:
|
200
|
+
|
201
|
+
knife azure server create
|
202
|
+
--azure-publish-settings-file '/path/to/your/cert.publishsettingsfile'
|
203
|
+
--azure-dns-name 'myserverdnsname'
|
204
|
+
--azure-service-location 'West US'
|
205
|
+
--azure-source-image 'windows-2012-image-id'
|
206
|
+
--winrm-user 'jetstream'
|
207
|
+
--winrm-password 'jetstream@123'
|
208
|
+
--distro 'windows-chef-client-msi'
|
209
|
+
|
210
|
+
Sample knife.rb for bootstrapping Windows Node with basic authentication
|
211
|
+
|
212
|
+
knife[:bootstrap_protocol] = 'winrm'
|
213
|
+
knife[:winrm_password] = 'mgcvTuvV2Rh'
|
214
|
+
knife[:winrm_user] = 'myuser'
|
215
|
+
knife[:winrm_port] = '5985'
|
216
|
+
knife[:distro] = 'windows-chef-client-msi'
|
217
|
+
knife[:azure_source_image]='windows-2012-image-id'
|
218
|
+
|
219
|
+
### Azure Server Delete Subcommand
|
220
|
+
Deletes an existing server in the currently configured Azure account. By
|
221
|
+
default, this does not delete the associated node and client objects from the
|
222
|
+
Chef server. To do so, add the --purge flag. Also by default, the DNS name, also called "cloud service", is deleted if you are deleting the last VM from that service. By default, the OS disk is also deleted. If you want to retain them add the --preserve flag as shown below. To delete the storage account, add the --delete-azure-storage-account flag since by default the storage account is not deleted.
|
223
|
+
|
224
|
+
knife azure server delete "myvm01"
|
225
|
+
knife azure server delete "myvm01" --purge #purge chef node
|
226
|
+
knife azure server delete "myvm01" --preserve-azure-os-disk
|
227
|
+
knife azure server delete "myvm01" --preserve-azure-dns-name
|
228
|
+
knife azure server delete "myvm01" --delete-azure-storage-account
|
229
|
+
|
230
|
+
Since the VM name can be the same across DNS name, you must specify the DNS
|
231
|
+
name also to delete the VM. Sample command to delete a VM for a given DNS name:
|
232
|
+
|
233
|
+
knife azure server delete "myvm01" --azure-dns-name "mydnsname"
|
234
|
+
knife azure server delete "myvm01" "myvm02" --azure-dns-name "mydnsname"
|
235
|
+
|
236
|
+
### Azure Server List Subcommand
|
237
|
+
Outputs a list of all servers in the currently configured Azure account. PLEASE NOTE - this shows all instances associated with the account, some of which may not be currently managed by the Chef server.
|
238
|
+
|
239
|
+
knife azure server list
|
240
|
+
|
241
|
+
## Alternative Management Certificate Specification
|
242
|
+
In addition to specifying the management certificate using the publishsettings
|
243
|
+
file, you can also specify it in PEM format. Follow these steps to generate the certificate in the PEM format:
|
244
|
+
|
245
|
+
1. Download the settings file from https://manage.windowsazure.com/publishsettings/index?client=xplat
|
246
|
+
2. Extract the data from the ManagementCertificate field into a separate file named - cert.pfx
|
247
|
+
3. Decode the certificate file:
|
248
|
+
|
249
|
+
#### On Linux/Mac(Homebrew)
|
250
|
+
|
251
|
+
base64 -d cert.pfx > cert_decoded.pfx
|
252
|
+
|
253
|
+
#### On Windows
|
254
|
+
You can decode and extract the PFX file using powershell or a free windows base 64 decoder such as http://www.fourmilab.ch/webtools/base64/base64.zip,
|
255
|
+
|
256
|
+
base64.exe -d cert.pfx -> cert_decoded.pfx
|
257
|
+
|
258
|
+
4. Convert the decoded PFX file to a PEM file
|
259
|
+
|
260
|
+
#### On Linux/Mac(Homebrew)
|
261
|
+
|
262
|
+
openssl pkcs12 -in cert_decoded.pfx -out managementCertificate.pem -nodes
|
263
|
+
|
264
|
+
#### On Windows
|
265
|
+
Use powershell & run following command. If openssl.exe is not already installed it can be downloaded from http://www.openssl.org/related/binaries.html (Note: openssl depends on Microsoft Visual C++ Redistributable package (x86) which must be installed for openssl to function properly).
|
266
|
+
|
267
|
+
openssl base64 -d -A -in cert_decoded.pfx -out cert_decode.der
|
268
|
+
|
269
|
+
openssl pkcs12 -in cert_decoded.der -out managementCertificate.pem -nodes
|
270
|
+
|
271
|
+
You might be asked to enter a password which is usually blank.
|
272
|
+
You might be also asked to enter a passphrase. Please enter the phrase of your choice.
|
273
|
+
|
274
|
+
It is possible to generate your own certificates and upload them. More Detailed Documentation about the Management Certificates is available : https://www.windowsazure.com/en-us/manage/linux/common-tasks/manage-certificates/
|
275
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Mukta Aphale (mukta.aphale@clogeny.com)
|
3
|
+
# Copyright:: Copyright (c) 2010-2011 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
|
+
class Azure
|
20
|
+
class Certificates
|
21
|
+
def initialize(connection)
|
22
|
+
@connection=connection
|
23
|
+
end
|
24
|
+
def create(params)
|
25
|
+
certificate = Certificate.new(@connection)
|
26
|
+
certificate.create(params)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Azure
|
32
|
+
class Certificate
|
33
|
+
attr_accessor :connection
|
34
|
+
attr_accessor :cert_data, :fingerprint, :certificate_version
|
35
|
+
def initialize(connection)
|
36
|
+
@connection = connection
|
37
|
+
@certificate_version = 2 # cf. RFC 5280 - to make it a "v3" certificate
|
38
|
+
end
|
39
|
+
def create(params)
|
40
|
+
# If RSA private key has been specified, then generate an x 509 certificate from the
|
41
|
+
# public part of the key
|
42
|
+
@cert_data = generate_public_key_certificate_data({:ssh_key => params[:identity_file],
|
43
|
+
:ssh_key_passphrase => params[:identity_file_passphrase]})
|
44
|
+
# Generate XML to call the API
|
45
|
+
# Add certificate to the hosted service
|
46
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
47
|
+
xml.CertificateFile('xmlns'=>'http://schemas.microsoft.com/windowsazure') {
|
48
|
+
xml.Data @cert_data
|
49
|
+
xml.CertificateFormat 'pfx'
|
50
|
+
xml.Password 'knifeazure'
|
51
|
+
}
|
52
|
+
end
|
53
|
+
# Windows Azure API call
|
54
|
+
@connection.query_azure("hostedservices/#{params[:azure_dns_name]}/certificates", "post", builder.to_xml)
|
55
|
+
# Return the fingerprint to be used while adding role
|
56
|
+
@fingerprint
|
57
|
+
end
|
58
|
+
|
59
|
+
def generate_public_key_certificate_data (params)
|
60
|
+
# Generate OpenSSL RSA key from the mentioned ssh key path (and passphrase)
|
61
|
+
key = OpenSSL::PKey::RSA.new(File.read(params[:ssh_key]), params[:ssh_key_passphrase])
|
62
|
+
# Generate X 509 certificate
|
63
|
+
ca = OpenSSL::X509::Certificate.new
|
64
|
+
ca.version = @certificate_version
|
65
|
+
ca.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
|
66
|
+
ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=knife-plugin/CN=Opscode CA"
|
67
|
+
ca.issuer = ca.subject # root CA's are "self-signed"
|
68
|
+
ca.public_key = key.public_key # Assign the ssh-key's public part to the certificate
|
69
|
+
ca.not_before = Time.now
|
70
|
+
ca.not_after = ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
|
71
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
72
|
+
ef.subject_certificate = ca
|
73
|
+
ef.issuer_certificate = ca
|
74
|
+
ca.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
|
75
|
+
ca.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
|
76
|
+
ca.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
|
77
|
+
ca.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
|
78
|
+
ca.sign(key, OpenSSL::Digest::SHA256.new)
|
79
|
+
# Generate the SHA1 fingerprint of the der format of the X 509 certificate
|
80
|
+
@fingerprint = OpenSSL::Digest::SHA1.new(ca.to_der)
|
81
|
+
# Create the pfx format of the certificate
|
82
|
+
pfx = OpenSSL::PKCS12.create('knifeazure', 'knife-azure-pfx', key, ca)
|
83
|
+
# Encode the pfx format - upload this certificate
|
84
|
+
Base64.strict_encode64(pfx.to_der)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/azure/connection.rb
CHANGED
@@ -23,11 +23,12 @@ require File.expand_path('../deploy', __FILE__)
|
|
23
23
|
require File.expand_path('../role', __FILE__)
|
24
24
|
require File.expand_path('../disk', __FILE__)
|
25
25
|
require File.expand_path('../image', __FILE__)
|
26
|
+
require File.expand_path('../certificate', __FILE__)
|
26
27
|
|
27
28
|
class Azure
|
28
29
|
class Connection
|
29
30
|
include AzureAPI
|
30
|
-
attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks, :storageaccounts
|
31
|
+
attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks, :storageaccounts, :certificates
|
31
32
|
def initialize(params={})
|
32
33
|
@rest = Rest.new(params)
|
33
34
|
@hosts = Hosts.new(self)
|
@@ -36,6 +37,7 @@ class Azure
|
|
36
37
|
@deploys = Deploys.new(self)
|
37
38
|
@roles = Roles.new(self)
|
38
39
|
@disks = Disks.new(self)
|
40
|
+
@certificates = Certificates.new(self)
|
39
41
|
end
|
40
42
|
def query_azure(service_name, verb = 'get', body = '')
|
41
43
|
Chef::Log.info 'calling ' + verb + ' ' + service_name
|
data/lib/azure/deploy.rb
CHANGED
@@ -21,40 +21,69 @@ class Azure
|
|
21
21
|
def initialize(connection)
|
22
22
|
@connection=connection
|
23
23
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
# force_load should be true when there is something in local cache and we want to reload
|
25
|
+
# first call is always load.
|
26
|
+
def load(force_load = false)
|
27
|
+
if not @deploys || force_load
|
28
|
+
@deploys = begin
|
29
|
+
deploys = Array.new
|
30
|
+
hosts = @connection.hosts.all
|
31
|
+
hosts.each do |host|
|
32
|
+
deploy = Deploy.new(@connection)
|
33
|
+
deploy.retrieve(host.name)
|
34
|
+
if deploy.name
|
35
|
+
host.add_deploy(deploy)
|
36
|
+
deploys << deploy
|
37
|
+
end
|
38
|
+
end
|
39
|
+
deploys
|
32
40
|
end
|
33
41
|
end
|
34
|
-
deploys
|
42
|
+
@deploys
|
43
|
+
end
|
44
|
+
|
45
|
+
def all
|
46
|
+
self.load
|
35
47
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
48
|
+
|
49
|
+
# TODO - Current knife-azure plug-in seems to have assumption that single hostedservice
|
50
|
+
# will always have one deployment (production). see Deploy#retrieve below
|
51
|
+
def get_deploy_name_for_hostedservice(hostedservicename)
|
52
|
+
host = @connection.hosts.find(hostedservicename)
|
53
|
+
if host && host.deploys.length > 0
|
54
|
+
host.deploys[0].name
|
55
|
+
else
|
56
|
+
nil
|
41
57
|
end
|
42
|
-
deployName
|
43
58
|
end
|
59
|
+
|
44
60
|
def create(params)
|
45
|
-
|
46
|
-
@connection.hosts.
|
47
|
-
|
48
|
-
|
61
|
+
if params[:azure_connect_to_existing_dns]
|
62
|
+
unless @connection.hosts.exists?(params[:azure_dns_name])
|
63
|
+
Chef::Log.fatal 'The specified Azure DNS Name does not exist.'
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
else
|
67
|
+
ret_val = @connection.hosts.create(params)
|
68
|
+
if ret_val.css('Error Code').length > 0
|
69
|
+
Chef::Log.fatal 'Unable to create DNS:' + ret_val.at_css('Error Code').content + ' : ' + ret_val.at_css('Error Message').content
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
unless @connection.storageaccounts.exists?(params[:azure_storage_account])
|
49
74
|
@connection.storageaccounts.create(params)
|
50
75
|
end
|
51
|
-
|
76
|
+
if params[:identity_file]
|
77
|
+
params[:fingerprint] = @connection.certificates.create(params)
|
78
|
+
end
|
79
|
+
params['deploy_name'] = get_deploy_name_for_hostedservice(params[:azure_dns_name])
|
80
|
+
|
52
81
|
if params['deploy_name'] != nil
|
53
82
|
role = Role.new(@connection)
|
54
83
|
roleXML = role.setup(params)
|
55
84
|
ret_val = role.create(params, roleXML)
|
56
85
|
else
|
57
|
-
params['deploy_name'] = params[:
|
86
|
+
params['deploy_name'] = params[:azure_dns_name]
|
58
87
|
deploy = Deploy.new(@connection)
|
59
88
|
deployXML = deploy.setup(params)
|
60
89
|
ret_val = deploy.create(params, deployXML)
|
@@ -63,7 +92,7 @@ class Azure
|
|
63
92
|
Chef::Log.fatal 'Unable to create role:' + ret_val.at_css('Error Code').content + ' : ' + ret_val.at_css('Error Message').content
|
64
93
|
exit 1
|
65
94
|
end
|
66
|
-
@connection.roles.
|
95
|
+
@connection.roles.find_in_hosted_service(params[:azure_vm_name], params[:azure_dns_name])
|
67
96
|
end
|
68
97
|
def delete(rolename)
|
69
98
|
end
|
@@ -71,7 +100,8 @@ class Azure
|
|
71
100
|
|
72
101
|
class Deploy
|
73
102
|
include AzureUtility
|
74
|
-
attr_accessor :connection, :name, :status, :url, :
|
103
|
+
attr_accessor :connection, :name, :status, :url, :hostedservicename
|
104
|
+
|
75
105
|
def initialize(connection)
|
76
106
|
@connection = connection
|
77
107
|
end
|
@@ -82,12 +112,12 @@ class Azure
|
|
82
112
|
@name = xml_content(deployXML, 'Deployment Name')
|
83
113
|
@status = xml_content(deployXML,'Deployment Status')
|
84
114
|
@url = xml_content(deployXML, 'Deployment Url')
|
85
|
-
@roles =
|
115
|
+
@roles = Hash.new
|
86
116
|
rolesXML = deployXML.css('Deployment RoleInstanceList RoleInstance')
|
87
117
|
rolesXML.each do |roleXML|
|
88
118
|
role = Role.new(@connection)
|
89
119
|
role.parse(roleXML, hostedservicename, @name)
|
90
|
-
@roles
|
120
|
+
@roles[role.name] = role
|
91
121
|
end
|
92
122
|
end
|
93
123
|
end
|
@@ -98,7 +128,7 @@ class Azure
|
|
98
128
|
builder = Nokogiri::XML::Builder.new do |xml|
|
99
129
|
xml.Deployment(
|
100
130
|
'xmlns'=>'http://schemas.microsoft.com/windowsazure',
|
101
|
-
'xmlns:i'=>'http://www.w3.org/2001/XMLSchema-instance'
|
131
|
+
'xmlns:i'=>'http://www.w3.org/2001/XMLSchema-instance'
|
102
132
|
) {
|
103
133
|
xml.Name params['deploy_name']
|
104
134
|
xml.DeploymentSlot 'Production'
|
@@ -110,8 +140,22 @@ class Azure
|
|
110
140
|
builder.doc
|
111
141
|
end
|
112
142
|
def create(params, deployXML)
|
113
|
-
servicecall = "hostedservices/#{params[:
|
114
|
-
@connection.query_azure(servicecall, "post", deployXML.to_xml)
|
143
|
+
servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments"
|
144
|
+
@connection.query_azure(servicecall, "post", deployXML.to_xml)
|
145
|
+
end
|
146
|
+
|
147
|
+
def roles
|
148
|
+
@roles.values if @roles
|
149
|
+
end
|
150
|
+
|
151
|
+
# just delete from local cache
|
152
|
+
def delete_role_if_present(role)
|
153
|
+
@roles.delete(role.name) if @roles
|
115
154
|
end
|
155
|
+
|
156
|
+
def find_role(name)
|
157
|
+
@roles[name] if @roles
|
158
|
+
end
|
159
|
+
|
116
160
|
end
|
117
161
|
end
|