knife-azure 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +22 -0
- data/Guardfile +8 -0
- data/LICENSE +201 -0
- data/Rakefile +51 -0
- data/knife-azure.gemspec +106 -0
- data/lib/azure/connection.rb +75 -0
- data/lib/azure/deploy.rb +114 -0
- data/lib/azure/disk.rb +62 -0
- data/lib/azure/host.rb +90 -0
- data/lib/azure/image.rb +58 -0
- data/lib/azure/rest.rb +97 -0
- data/lib/azure/role.rb +182 -0
- data/lib/azure/utility.rb +29 -0
- data/lib/chef/knife/azure_base.rb +102 -0
- data/lib/chef/knife/azure_image_list.rb +58 -0
- data/lib/chef/knife/azure_server_create.rb +283 -0
- data/lib/chef/knife/azure_server_delete.rb +103 -0
- data/lib/chef/knife/azure_server_describe.rb +85 -0
- data/lib/chef/knife/azure_server_list.rb +70 -0
- data/lib/knife-azure/version.rb +7 -0
- data/readme.rdoc +210 -0
- data/spec/functional/deploys_test.rb +39 -0
- data/spec/functional/host_test.rb +22 -0
- data/spec/functional/images_list_test.rb +44 -0
- data/spec/functional/role_test.rb +16 -0
- data/spec/integration/role_lifecycle_test.rb +60 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/unit/assets/create_deployment.xml +37 -0
- data/spec/unit/assets/create_deployment_in_progress.xml +1 -0
- data/spec/unit/assets/create_host.xml +7 -0
- data/spec/unit/assets/create_role.xml +54 -0
- data/spec/unit/assets/list_deployments_for_service000.xml +126 -0
- data/spec/unit/assets/list_deployments_for_service001.xml +166 -0
- data/spec/unit/assets/list_deployments_for_service002.xml +1 -0
- data/spec/unit/assets/list_deployments_for_service003.xml +1 -0
- data/spec/unit/assets/list_disks.xml +1 -0
- data/spec/unit/assets/list_hosts.xml +1 -0
- data/spec/unit/assets/list_images.xml +1 -0
- data/spec/unit/assets/post_success.xml +6 -0
- data/spec/unit/deploys_list_spec.rb +55 -0
- data/spec/unit/disks_spec.rb +44 -0
- data/spec/unit/hosts_spec.rb +55 -0
- data/spec/unit/images_spec.rb +35 -0
- data/spec/unit/query_azure_mock.rb +69 -0
- data/spec/unit/roles_create_spec.rb +82 -0
- data/spec/unit/roles_list_spec.rb +32 -0
- metadata +240 -0
data/lib/azure/host.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Barry Davis (barryd@jetstreamsoftware.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 Hosts
|
21
|
+
def initialize(connection)
|
22
|
+
@connection=connection
|
23
|
+
end
|
24
|
+
def all
|
25
|
+
hosted_services = Array.new
|
26
|
+
responseXML = @connection.query_azure('hostedservices')
|
27
|
+
servicesXML = responseXML.css('HostedServices HostedService')
|
28
|
+
servicesXML.each do |serviceXML|
|
29
|
+
host = Host.new(@connection)
|
30
|
+
hosted_services << host.parse(serviceXML)
|
31
|
+
end
|
32
|
+
hosted_services
|
33
|
+
end
|
34
|
+
def exists(name)
|
35
|
+
hostExists = false
|
36
|
+
self.all.each do |host|
|
37
|
+
next unless host.name == name
|
38
|
+
hostExists = true
|
39
|
+
end
|
40
|
+
hostExists
|
41
|
+
end
|
42
|
+
def create(params)
|
43
|
+
host = Host.new(@connection)
|
44
|
+
host.create(params)
|
45
|
+
end
|
46
|
+
def delete(name)
|
47
|
+
if self.exists name
|
48
|
+
servicecall = "hostedservices/" + name
|
49
|
+
@connection.query_azure(servicecall, "delete")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Azure
|
56
|
+
class Host
|
57
|
+
include AzureUtility
|
58
|
+
attr_accessor :connection, :name, :url, :label
|
59
|
+
attr_accessor :dateCreated, :description, :location
|
60
|
+
attr_accessor :dateModified, :status
|
61
|
+
def initialize(connection)
|
62
|
+
@connection = connection
|
63
|
+
end
|
64
|
+
def parse(serviceXML)
|
65
|
+
@name = xml_content(serviceXML, 'ServiceName')
|
66
|
+
@url = xml_content(serviceXML, 'Url')
|
67
|
+
@label = xml_content(serviceXML, 'HostedServiceProperties Label')
|
68
|
+
@dateCreated = xml_content(serviceXML, 'HostedServiceProperties DateCreated')
|
69
|
+
@description = xml_content(serviceXML, 'HostedServiceProperties Description')
|
70
|
+
@location = xml_content(serviceXML, 'HostedServiceProperties Location')
|
71
|
+
@dateModified = xml_content(serviceXML, 'HostedServiceProperties DateLastModified')
|
72
|
+
@status = xml_content(serviceXML, 'HostedServiceProperties Status')
|
73
|
+
self
|
74
|
+
end
|
75
|
+
def create(params)
|
76
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
77
|
+
xml.CreateHostedService('xmlns'=>'http://schemas.microsoft.com/windowsazure') {
|
78
|
+
xml.ServiceName params[:hosted_service_name]
|
79
|
+
xml.Label Base64.encode64(params[:hosted_service_name])
|
80
|
+
xml.Description params['hosted_service_description'] || 'Explicitly created hosted service'
|
81
|
+
xml.Location params['hosted_service_location'] || 'Windows Azure Preview'
|
82
|
+
}
|
83
|
+
end
|
84
|
+
@connection.query_azure("hostedservices", "post", builder.to_xml)
|
85
|
+
end
|
86
|
+
def details
|
87
|
+
response = @connection.query_azure('hostedservices/' + @name + '?embed-detail=true')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/azure/image.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Barry Davis (barryd@jetstreamsoftware.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 Images
|
21
|
+
def initialize(connection)
|
22
|
+
@connection=connection
|
23
|
+
end
|
24
|
+
def all
|
25
|
+
images = Array.new
|
26
|
+
response = @connection.query_azure('images')
|
27
|
+
osimages = response.css('OSImage')
|
28
|
+
osimages.each do |image|
|
29
|
+
item = Image.new(image)
|
30
|
+
images << item
|
31
|
+
end
|
32
|
+
images
|
33
|
+
end
|
34
|
+
def exists(name)
|
35
|
+
imageExists = false
|
36
|
+
self.all.each do |host|
|
37
|
+
next unless host.name == name
|
38
|
+
imageExists = true
|
39
|
+
end
|
40
|
+
imageExists
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Azure
|
46
|
+
class Image
|
47
|
+
attr_accessor :category, :label
|
48
|
+
attr_accessor :name, :os, :eula, :description
|
49
|
+
def initialize(image)
|
50
|
+
@category = image.at_css('Category').content
|
51
|
+
@label = image.at_css('Label').content
|
52
|
+
@name = image.at_css('Name').content
|
53
|
+
@os = image.at_css('OS').content
|
54
|
+
@eula = image.at_css('Eula').content
|
55
|
+
@description = image.at_css('Description').content
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/azure/rest.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Barry Davis (barryd@jetstreamsoftware.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
|
+
require "net/https"
|
19
|
+
require "uri"
|
20
|
+
require "nokogiri"
|
21
|
+
|
22
|
+
module AzureAPI
|
23
|
+
|
24
|
+
class Rest
|
25
|
+
def initialize(params)
|
26
|
+
@subscription_id = params[:azure_subscription_id]
|
27
|
+
@pem_file = File.read find_pem(params[:azure_pem_file])
|
28
|
+
@host_name = params[:azure_host_name]
|
29
|
+
end
|
30
|
+
def find_pem(name)
|
31
|
+
config_dir = Chef::Knife.chef_config_dir
|
32
|
+
if File.exist? name
|
33
|
+
pem_file = name
|
34
|
+
elsif config_dir && File.exist?(File.join(config_dir, name))
|
35
|
+
pem_file = File.join(config_dir, name)
|
36
|
+
elsif File.exist?(File.join(ENV['HOME'], '.chef', name))
|
37
|
+
pem_file = File.join(ENV['HOME'], '.chef', name)
|
38
|
+
else
|
39
|
+
raise 'Unable to find certificate pem file - ' + name
|
40
|
+
end
|
41
|
+
pem_file
|
42
|
+
end
|
43
|
+
def query_azure(service_name, verb = 'get', body = '')
|
44
|
+
request_url = "https://#{@host_name}/#{@subscription_id}/services/#{service_name}"
|
45
|
+
print '.'
|
46
|
+
uri = URI.parse(request_url)
|
47
|
+
http = http_setup(uri)
|
48
|
+
request = request_setup(uri, verb, body)
|
49
|
+
response = http.request(request)
|
50
|
+
@last_request_id = response['x-ms-request-id']
|
51
|
+
response
|
52
|
+
end
|
53
|
+
def query_for_completion()
|
54
|
+
request_url = "https://#{@host_name}/#{@subscription_id}/operations/#{@last_request_id}"
|
55
|
+
uri = URI.parse(request_url)
|
56
|
+
http = http_setup(uri)
|
57
|
+
request = request_setup(uri, 'get', '')
|
58
|
+
response = http.request(request)
|
59
|
+
end
|
60
|
+
def http_setup(uri)
|
61
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
62
|
+
store = OpenSSL::X509::Store.new
|
63
|
+
store.add_cert(OpenSSL::X509::Certificate.new(File.read(find_pem("cacert.pem"))))
|
64
|
+
http.cert_store = store
|
65
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
66
|
+
http.use_ssl = true
|
67
|
+
http.cert = OpenSSL::X509::Certificate.new(@pem_file)
|
68
|
+
http.key = OpenSSL::PKey::RSA.new(@pem_file)
|
69
|
+
http
|
70
|
+
end
|
71
|
+
def request_setup(uri, verb, body)
|
72
|
+
if verb == 'get'
|
73
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
74
|
+
elsif verb == 'post'
|
75
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
76
|
+
elsif verb == 'delete'
|
77
|
+
request = Net::HTTP::Delete.new(uri.request_uri)
|
78
|
+
end
|
79
|
+
request["x-ms-version"] = "2012-03-01"
|
80
|
+
request["content-type"] = "application/xml"
|
81
|
+
request["accept"] = "application/xml"
|
82
|
+
request["accept-charset"] = "utf-8"
|
83
|
+
request.body = body
|
84
|
+
request
|
85
|
+
end
|
86
|
+
def showResponse(response)
|
87
|
+
puts "=== response body ==="
|
88
|
+
puts response.body
|
89
|
+
puts "=== response.code ==="
|
90
|
+
puts response.code
|
91
|
+
puts "=== response.inspect ==="
|
92
|
+
puts response.inspect
|
93
|
+
puts "=== all of the headers ==="
|
94
|
+
puts response.each_header { |h, j| puts h.inspect + ' : ' + j.inspect}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/azure/role.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Barry Davis (barryd@jetstreamsoftware.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 Roles
|
21
|
+
attr_accessor :connection, :roles
|
22
|
+
def initialize(connection)
|
23
|
+
@connection = connection
|
24
|
+
@roles = nil
|
25
|
+
end
|
26
|
+
def all
|
27
|
+
@roles = Array.new
|
28
|
+
@connection.deploys.all.each do |deploy|
|
29
|
+
deploy.roles.each do |role|
|
30
|
+
@roles << role
|
31
|
+
end
|
32
|
+
end
|
33
|
+
@roles
|
34
|
+
end
|
35
|
+
def find(name)
|
36
|
+
if @roles == nil
|
37
|
+
all
|
38
|
+
end
|
39
|
+
@roles.each do |role|
|
40
|
+
if(role.name == name)
|
41
|
+
return role
|
42
|
+
end
|
43
|
+
end
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
def alone_on_host(name)
|
47
|
+
found_role = find(name)
|
48
|
+
@roles.each do |role|
|
49
|
+
if (role.name != found_role.name &&
|
50
|
+
role.deployname == found_role.deployname &&
|
51
|
+
role.hostedservicename == found_role.hostedservicename)
|
52
|
+
return false;
|
53
|
+
end
|
54
|
+
end
|
55
|
+
true
|
56
|
+
end
|
57
|
+
def exists(name)
|
58
|
+
find(name) != nil
|
59
|
+
end
|
60
|
+
def delete(name)
|
61
|
+
role = find(name)
|
62
|
+
if role != nil
|
63
|
+
if alone_on_host(name)
|
64
|
+
servicecall = "hostedservices/#{role.hostedservicename}/deployments" +
|
65
|
+
"/#{role.deployname}"
|
66
|
+
else
|
67
|
+
servicecall = "hostedservices/#{role.hostedservicename}/deployments" +
|
68
|
+
"/#{role.deployname}/roles/#{role.name}"
|
69
|
+
end
|
70
|
+
@connection.query_azure(servicecall, "delete")
|
71
|
+
end
|
72
|
+
#@connection.disks.clear_unattached
|
73
|
+
end
|
74
|
+
end
|
75
|
+
class Role
|
76
|
+
include AzureUtility
|
77
|
+
attr_accessor :connection, :name, :status, :size, :ipaddress
|
78
|
+
attr_accessor :sshport, :sshipaddress, :hostedservicename, :deployname
|
79
|
+
attr_accessor :hostname, :tcpports, :udpports
|
80
|
+
def initialize(connection)
|
81
|
+
@connection = connection
|
82
|
+
end
|
83
|
+
def parse(roleXML, hostedservicename, deployname)
|
84
|
+
@name = xml_content(roleXML, 'RoleName')
|
85
|
+
@status = xml_content(roleXML, 'InstanceStatus')
|
86
|
+
@size = xml_content(roleXML, 'InstanceSize')
|
87
|
+
@ipaddress = xml_content(roleXML, 'IpAddress')
|
88
|
+
@hostname = xml_content(roleXML, 'HostName')
|
89
|
+
@hostedservicename = hostedservicename
|
90
|
+
@deployname = deployname
|
91
|
+
@tcpports = Array.new
|
92
|
+
@udpports = Array.new
|
93
|
+
endpoints = roleXML.css('InstanceEndpoint')
|
94
|
+
endpoints.each do |endpoint|
|
95
|
+
if xml_content(endpoint, 'Name').downcase == 'ssh'
|
96
|
+
@sshport = xml_content(endpoint, 'PublicPort')
|
97
|
+
@sshipaddress = xml_content(endpoint, 'Vip')
|
98
|
+
else
|
99
|
+
hash = Hash.new
|
100
|
+
hash['Name'] = xml_content(endpoint, 'Name')
|
101
|
+
hash['Vip'] = xml_content(endpoint, 'Vip')
|
102
|
+
hash['PublicPort'] = xml_content(endpoint, 'PublicPort')
|
103
|
+
hash['LocalPort'] = xml_content(endpoint, 'LocalPort')
|
104
|
+
if xml_content(endpoint, 'Protocol') == 'tcp'
|
105
|
+
@tcpports << hash
|
106
|
+
else # == 'udp'
|
107
|
+
@udpports << hash
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
def setup(params)
|
113
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
114
|
+
xml.PersistentVMRole(
|
115
|
+
'xmlns'=>'http://schemas.microsoft.com/windowsazure',
|
116
|
+
'xmlns:i'=>'http://www.w3.org/2001/XMLSchema-instance'
|
117
|
+
) {
|
118
|
+
xml.RoleName {xml.text params[:role_name]}
|
119
|
+
xml.OsVersion('i:nil' => 'true')
|
120
|
+
xml.RoleType 'PersistentVMRole'
|
121
|
+
xml.ConfigurationSets {
|
122
|
+
xml.ConfigurationSet('i:type' => 'LinuxProvisioningConfigurationSet') {
|
123
|
+
xml.ConfigurationSetType 'LinuxProvisioningConfiguration'
|
124
|
+
xml.HostName params[:host_name]
|
125
|
+
xml.UserName params[:ssh_user]
|
126
|
+
xml.UserPassword params[:ssh_password]
|
127
|
+
xml.DisableSshPasswordAuthentication 'false'
|
128
|
+
}
|
129
|
+
xml.ConfigurationSet('i:type' => 'NetworkConfigurationSet') {
|
130
|
+
xml.ConfigurationSetType 'NetworkConfiguration'
|
131
|
+
xml.InputEndpoints {
|
132
|
+
xml.InputEndpoint {
|
133
|
+
xml.LocalPort '22'
|
134
|
+
xml.Name 'SSH'
|
135
|
+
xml.Protocol 'TCP'
|
136
|
+
}
|
137
|
+
if params[:tcp_endpoints]
|
138
|
+
params[:tcp_endpoints].split(',').each do |endpoint|
|
139
|
+
ports = endpoint.split(':')
|
140
|
+
xml.InputEndpoint {
|
141
|
+
xml.LocalPort ports[0]
|
142
|
+
xml.Name 'tcpport_' + ports[0] + '_' + params[:host_name]
|
143
|
+
if ports.length > 1
|
144
|
+
xml.Port ports[1]
|
145
|
+
end
|
146
|
+
xml.Protocol 'TCP'
|
147
|
+
}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
if params[:udp_endpoints]
|
151
|
+
params[:udp_endpoints].split(',').each do |endpoint|
|
152
|
+
ports = endpoint.split(':')
|
153
|
+
xml.InputEndpoint {
|
154
|
+
xml.LocalPort ports[0]
|
155
|
+
xml.Name 'udpport_' + ports[0] + '_' + params[:host_name]
|
156
|
+
if ports.length > 1
|
157
|
+
xml.Port ports[1]
|
158
|
+
end
|
159
|
+
xml.Protocol 'UDP'
|
160
|
+
}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
}
|
164
|
+
}
|
165
|
+
}
|
166
|
+
xml.Label Base64.encode64(params[:role_name]).strip
|
167
|
+
xml.OSVirtualHardDisk {
|
168
|
+
xml.MediaLink 'http://' + params[:media_location_prefix] + 'imagestore.blob.core.azure-preview.com/os-disks/' + (params[:os_disk_name] || Time.now.strftime('disk_%Y_%m_%d_%H_%M'))
|
169
|
+
xml.SourceImageName params[:source_image]
|
170
|
+
}
|
171
|
+
xml.RoleSize params[:role_size]
|
172
|
+
}
|
173
|
+
end
|
174
|
+
builder.doc
|
175
|
+
end
|
176
|
+
def create(params, roleXML)
|
177
|
+
servicecall = "hostedservices/#{params[:hosted_service_name]}/deployments" +
|
178
|
+
"/#{params['deploy_name']}/roles"
|
179
|
+
@connection.query_azure(servicecall, "post", roleXML.to_xml)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Barry Davis (barryd@jetstreamsoftware.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
|
+
module AzureUtility
|
20
|
+
def xml_content(xml, key, default='')
|
21
|
+
content = default
|
22
|
+
node = xml.at_css(key)
|
23
|
+
if node
|
24
|
+
content = node.content
|
25
|
+
end
|
26
|
+
content
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,102 @@
|
|
1
|
+
|
2
|
+
# Author:: Barry Davis (barryd@jetstreamsoftware.com)
|
3
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'chef/knife'
|
21
|
+
require File.expand_path('../../../azure/connection', __FILE__)
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Knife
|
25
|
+
module AzureBase
|
26
|
+
|
27
|
+
# :nodoc:
|
28
|
+
# Would prefer to do this in a rational way, but can't be done b/c of
|
29
|
+
# Mixlib::CLI's design :(
|
30
|
+
def self.included(includer)
|
31
|
+
includer.class_eval do
|
32
|
+
|
33
|
+
deps do
|
34
|
+
require 'readline'
|
35
|
+
require 'chef/json_compat'
|
36
|
+
end
|
37
|
+
|
38
|
+
option :azure_subscription_id,
|
39
|
+
:short => "-S ID",
|
40
|
+
:long => "--azure-subscription-id ID",
|
41
|
+
:description => "Your Azure subscription ID",
|
42
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:azure_subscription_id] = key }
|
43
|
+
|
44
|
+
option :azure_pem_file,
|
45
|
+
:short => "-p FILENAME",
|
46
|
+
:long => "--azure-pem-filename FILENAME",
|
47
|
+
:description => "Your Azure PEM file name",
|
48
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:azure_pem_file] = key }
|
49
|
+
|
50
|
+
option :azure_host_name,
|
51
|
+
:short => "-H HOSTNAME",
|
52
|
+
:long => "--azure_host_name HOSTNAME",
|
53
|
+
:description => "Your Azure host name",
|
54
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:azure_host_name] = key }
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def connection
|
60
|
+
@connection ||= begin
|
61
|
+
connection = Azure::Connection.new(
|
62
|
+
:azure_subscription_id =>
|
63
|
+
config[:azure_subscription_id] || Chef::Config[:knife][:azure_subscription_id],
|
64
|
+
:azure_pem_file =>
|
65
|
+
config[:azure_pem_file] || Chef::Config[:knife][:azure_pem_file],
|
66
|
+
:azure_host_name =>
|
67
|
+
config[:azure_host_name] || Chef::Config[:knife][:azure_host_name]
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def locate_config_value(key)
|
73
|
+
key = key.to_sym
|
74
|
+
config[key] || Chef::Config[:knife][key]
|
75
|
+
end
|
76
|
+
|
77
|
+
def msg_pair(label, value, color=:cyan)
|
78
|
+
if value && !value.to_s.empty?
|
79
|
+
puts "#{ui.color(label, color)}: #{value}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate!(keys=[:azure_subscription_id, :azure_pem_file, :azure_host_name])
|
84
|
+
errors = []
|
85
|
+
|
86
|
+
keys.each do |k|
|
87
|
+
pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
|
88
|
+
if Chef::Config[:knife][k].nil?
|
89
|
+
errors << "You did not provide a valid '#{pretty_key}' value."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
if errors.each{|e| ui.error(e)}.any?
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Barry Davis (barryd@jetstreamsoftware.com)
|
3
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
4
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
5
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
require File.expand_path('../azure_base', __FILE__)
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Knife
|
25
|
+
class AzureImageList < Knife
|
26
|
+
|
27
|
+
include Knife::AzureBase
|
28
|
+
|
29
|
+
banner "knife azure image list (options)"
|
30
|
+
|
31
|
+
def run
|
32
|
+
$stdout.sync = true
|
33
|
+
|
34
|
+
validate!
|
35
|
+
|
36
|
+
image_list = [
|
37
|
+
ui.color('Name', :bold),
|
38
|
+
#ui.color('Category', :bold),
|
39
|
+
#ui.color('Label', :bold),
|
40
|
+
#ui.color('OS', :bold),
|
41
|
+
#ui.color('Eula', :bold),
|
42
|
+
]
|
43
|
+
items = connection.images.all
|
44
|
+
items.each do |image|
|
45
|
+
if image.os == 'Linux'
|
46
|
+
image_list << image.name.to_s
|
47
|
+
#image_list << image.category.to_s
|
48
|
+
#image_list << image.label.to_s
|
49
|
+
#image_list << image.os.to_s
|
50
|
+
#image_list << image.eula.to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
puts ''
|
54
|
+
puts ui.list(image_list, :columns_across, 1)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|