Merge branch 'master' of github.com:opscode/knife-azure into OC-7832
Conflicts: lib/chef/knife/azure_server_list.rb
This commit is contained in:
Коммит
bd9f0ce2f3
1
Rakefile
1
Rakefile
|
@ -24,7 +24,6 @@ require 'rspec/core'
|
|||
require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new(:spec) do |spec|
|
||||
spec.pattern = FileList['spec/unit/**/*_spec.rb']
|
||||
touch "AzureLinuxCert.pem"
|
||||
end
|
||||
|
||||
RSpec::Core::RakeTask.new(:functional) do |spec|
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
#
|
||||
# Author:: Mukta Aphale (mukta.aphale@clogeny.com)
|
||||
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
||||
# License:: Apache License, Version 2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
class Azure
|
||||
class Certificates
|
||||
def initialize(connection)
|
||||
@connection=connection
|
||||
end
|
||||
def create(params)
|
||||
certificate = Certificate.new(@connection)
|
||||
certificate.create(params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Azure
|
||||
class Certificate
|
||||
attr_accessor :connection
|
||||
attr_accessor :cert_data, :fingerprint, :certificate_version
|
||||
def initialize(connection)
|
||||
@connection = connection
|
||||
@certificate_version = 2 # cf. RFC 5280 - to make it a "v3" certificate
|
||||
end
|
||||
def create(params)
|
||||
# If RSA private key has been specified, then generate an x 509 certificate from the
|
||||
# public part of the key
|
||||
@cert_data = generate_public_key_certificate_data({:ssh_key => params[:identity_file],
|
||||
:ssh_key_passphrase => params[:identity_file_passphrase]})
|
||||
# Generate XML to call the API
|
||||
# Add certificate to the hosted service
|
||||
builder = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.CertificateFile('xmlns'=>'http://schemas.microsoft.com/windowsazure') {
|
||||
xml.Data @cert_data
|
||||
xml.CertificateFormat 'pfx'
|
||||
xml.Password 'knifeazure'
|
||||
}
|
||||
end
|
||||
# Windows Azure API call
|
||||
@connection.query_azure("hostedservices/#{params[:hosted_service_name]}/certificates", "post", builder.to_xml)
|
||||
# Return the fingerprint to be used while adding role
|
||||
@fingerprint
|
||||
end
|
||||
|
||||
def generate_public_key_certificate_data (params)
|
||||
# Generate OpenSSL RSA key from the mentioned ssh key path (and passphrase)
|
||||
key = OpenSSL::PKey::RSA.new(File.read(params[:ssh_key]), params[:ssh_key_passphrase])
|
||||
# Generate X 509 certificate
|
||||
ca = OpenSSL::X509::Certificate.new
|
||||
ca.version = @certificate_version
|
||||
ca.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
|
||||
ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=knife-plugin/CN=Opscode CA"
|
||||
ca.issuer = ca.subject # root CA's are "self-signed"
|
||||
ca.public_key = key.public_key # Assign the ssh-key's public part to the certificate
|
||||
ca.not_before = Time.now
|
||||
ca.not_after = ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
|
||||
ef = OpenSSL::X509::ExtensionFactory.new
|
||||
ef.subject_certificate = ca
|
||||
ef.issuer_certificate = ca
|
||||
ca.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
|
||||
ca.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
|
||||
ca.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
|
||||
ca.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
|
||||
ca.sign(key, OpenSSL::Digest::SHA256.new)
|
||||
# Generate the SHA1 fingerprint of the der format of the X 509 certificate
|
||||
@fingerprint = OpenSSL::Digest::SHA1.new(ca.to_der)
|
||||
# Create the pfx format of the certificate
|
||||
pfx = OpenSSL::PKCS12.create('knifeazure', 'knife-azure-pfx', key, ca)
|
||||
# Encode the pfx format - upload this certificate
|
||||
Base64.strict_encode64(pfx.to_der)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -23,11 +23,12 @@ require File.expand_path('../deploy', __FILE__)
|
|||
require File.expand_path('../role', __FILE__)
|
||||
require File.expand_path('../disk', __FILE__)
|
||||
require File.expand_path('../image', __FILE__)
|
||||
require File.expand_path('../certificate', __FILE__)
|
||||
|
||||
class Azure
|
||||
class Connection
|
||||
include AzureAPI
|
||||
attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks, :storageaccounts
|
||||
attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks, :storageaccounts, :certificates
|
||||
def initialize(params={})
|
||||
@rest = Rest.new(params)
|
||||
@hosts = Hosts.new(self)
|
||||
|
@ -36,6 +37,7 @@ class Azure
|
|||
@deploys = Deploys.new(self)
|
||||
@roles = Roles.new(self)
|
||||
@disks = Disks.new(self)
|
||||
@certificates = Certificates.new(self)
|
||||
end
|
||||
def query_azure(service_name, verb = 'get', body = '')
|
||||
Chef::Log.info 'calling ' + verb + ' ' + service_name
|
||||
|
|
|
@ -48,6 +48,9 @@ class Azure
|
|||
unless @connection.storageaccounts.exists(params[:storage_account])
|
||||
@connection.storageaccounts.create(params)
|
||||
end
|
||||
if params[:identity_file]
|
||||
params[:fingerprint] = @connection.certificates.create(params)
|
||||
end
|
||||
params['deploy_name'] = find(params[:hosted_service_name])
|
||||
if params['deploy_name'] != nil
|
||||
role = Role.new(@connection)
|
||||
|
|
|
@ -33,7 +33,20 @@ class Azure
|
|||
end
|
||||
@roles
|
||||
end
|
||||
def find(name)
|
||||
|
||||
def find_in_hosted_service(name, hostedservicename)
|
||||
find_roles_with_hostedservice(hostedservicename).each do | role |
|
||||
if (role.name == name)
|
||||
return role
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def find(name, params= nil)
|
||||
if params && params[:azure_hosted_service_name]
|
||||
return find_in_hosted_service(name, params[:azure_hosted_service_name])
|
||||
end
|
||||
if @roles == nil
|
||||
all
|
||||
end
|
||||
|
@ -44,8 +57,7 @@ class Azure
|
|||
end
|
||||
nil
|
||||
end
|
||||
def alone_on_host(name)
|
||||
found_role = find(name)
|
||||
def alone_on_host(found_role)
|
||||
@roles.each do |role|
|
||||
if (role.name != found_role.name &&
|
||||
role.deployname == found_role.deployname &&
|
||||
|
@ -61,7 +73,7 @@ class Azure
|
|||
def delete(name, params)
|
||||
role = find(name)
|
||||
if role != nil
|
||||
if alone_on_host(name)
|
||||
if alone_on_host(role)
|
||||
servicecall = "hostedservices/#{role.hostedservicename}/deployments" +
|
||||
"/#{role.deployname}"
|
||||
else
|
||||
|
@ -69,12 +81,24 @@ class Azure
|
|||
"/#{role.deployname}/roles/#{role.name}"
|
||||
end
|
||||
roleXML = nil
|
||||
if params[:purge_os_disk]
|
||||
unless params[:preserve_os_disk]
|
||||
roleXML = @connection.query_azure(servicecall, "get")
|
||||
end
|
||||
@connection.query_azure(servicecall, "delete")
|
||||
# delete role from local cache as well.
|
||||
@roles.delete(role)
|
||||
|
||||
if params[:purge_os_disk]
|
||||
unless params[:preserve_hosted_service]
|
||||
unless params[:hostedservicename].nil?
|
||||
roles_using_same_service = find_roles_with_hostedservice(params[:hostedservicename])
|
||||
if roles_using_same_service.size <= 1
|
||||
servicecall = "hostedservices/" + params[:hostedservicename]
|
||||
@connection.query_azure(servicecall, "delete")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless params[:preserve_os_disk]
|
||||
osdisk = roleXML.css(roleXML, 'OSVirtualHardDisk')
|
||||
disk_name = xml_content(osdisk, 'DiskName')
|
||||
servicecall = "disks/#{disk_name}"
|
||||
|
@ -82,12 +106,24 @@ class Azure
|
|||
end
|
||||
end
|
||||
end
|
||||
def find_roles_with_hostedservice(hostedservicename)
|
||||
if @roles == nil
|
||||
all
|
||||
end
|
||||
return_roles = Array.new
|
||||
@roles.each do |role|
|
||||
if(role.hostedservicename == hostedservicename)
|
||||
return_roles << role
|
||||
end
|
||||
end
|
||||
return_roles
|
||||
end
|
||||
end
|
||||
class Role
|
||||
include AzureUtility
|
||||
attr_accessor :connection, :name, :status, :size, :ipaddress
|
||||
attr_accessor :sshport, :sshipaddress, :hostedservicename, :deployname
|
||||
attr_accessor :winrmport, :winrmipaddress
|
||||
attr_accessor :connection, :name, :status, :size, :ipaddress, :publicipaddress
|
||||
attr_accessor :sshport, :hostedservicename, :deployname
|
||||
attr_accessor :winrmport
|
||||
attr_accessor :hostname, :tcpports, :udpports
|
||||
|
||||
def initialize(connection)
|
||||
|
@ -105,13 +141,12 @@ class Azure
|
|||
@udpports = Array.new
|
||||
|
||||
endpoints = roleXML.css('InstanceEndpoint')
|
||||
@publicipaddress = xml_content(endpoints[0], 'Vip') if !endpoints.empty?
|
||||
endpoints.each do |endpoint|
|
||||
if xml_content(endpoint, 'Name').downcase == 'ssh'
|
||||
@sshport = xml_content(endpoint, 'PublicPort')
|
||||
@sshipaddress = xml_content(endpoint, 'Vip')
|
||||
elsif xml_content(endpoint, 'Name').downcase == 'winrm'
|
||||
@winrmport = xml_content(endpoint, 'PublicPort')
|
||||
@winrmipaddress = xml_content(endpoint, 'Vip')
|
||||
else
|
||||
hash = Hash.new
|
||||
hash['Name'] = xml_content(endpoint, 'Name')
|
||||
|
@ -141,9 +176,21 @@ class Azure
|
|||
xml.ConfigurationSet('i:type' => 'LinuxProvisioningConfigurationSet') {
|
||||
xml.ConfigurationSetType 'LinuxProvisioningConfiguration'
|
||||
xml.HostName params[:host_name]
|
||||
xml.UserName params[:ssh_user]
|
||||
xml.UserPassword params[:ssh_password]
|
||||
xml.DisableSshPasswordAuthentication 'false'
|
||||
xml.UserName params[:ssh_user]
|
||||
unless params[:identity_file].nil?
|
||||
xml.DisableSshPasswordAuthentication 'true'
|
||||
xml.SSH {
|
||||
xml.PublicKeys {
|
||||
xml.PublicKey {
|
||||
xml.Fingerprint params[:fingerprint]
|
||||
xml.Path '/home/' + params[:ssh_user] + '/.ssh/authorized_keys'
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
xml.UserPassword params[:ssh_password]
|
||||
xml.DisableSshPasswordAuthentication 'false'
|
||||
end
|
||||
}
|
||||
elsif params[:os_type] == 'Windows'
|
||||
xml.ConfigurationSet('i:type' => 'WindowsProvisioningConfigurationSet') {
|
||||
|
|
|
@ -49,7 +49,7 @@ class Chef
|
|||
|
||||
option :azure_host_name,
|
||||
:short => "-H HOSTNAME",
|
||||
:long => "--azure_host_name HOSTNAME",
|
||||
:long => "--azure-host-name HOSTNAME",
|
||||
:description => "Your Azure host name",
|
||||
:proc => Proc.new { |key| Chef::Config[:knife][:azure_host_name] = key }
|
||||
|
||||
|
|
|
@ -31,13 +31,18 @@ class Chef
|
|||
require 'readline'
|
||||
require 'chef/json_compat'
|
||||
require 'chef/knife/bootstrap'
|
||||
require 'chef/knife/bootstrap_windows_winrm'
|
||||
require 'chef/knife/bootstrap_windows_ssh'
|
||||
require 'chef/knife/core/windows_bootstrap_context'
|
||||
require 'chef/knife/winrm'
|
||||
Chef::Knife::Bootstrap.load_deps
|
||||
end
|
||||
|
||||
def load_winrm_deps
|
||||
require 'winrm'
|
||||
require 'em-winrm'
|
||||
require 'chef/knife/winrm'
|
||||
require 'chef/knife/bootstrap_windows_winrm'
|
||||
end
|
||||
|
||||
banner "knife azure server create (options)"
|
||||
|
||||
attr_accessor :initial_sleep_delay
|
||||
|
@ -62,11 +67,6 @@ class Chef
|
|||
:long => "--ssh-password PASSWORD",
|
||||
:description => "The ssh password"
|
||||
|
||||
option :identity_file,
|
||||
:short => "-i IDENTITY_FILE",
|
||||
:long => "--identity-file IDENTITY_FILE",
|
||||
:description => "The SSH identity file used for authentication"
|
||||
|
||||
option :prerelease,
|
||||
:long => "--prerelease",
|
||||
:description => "Install the pre-release chef gems"
|
||||
|
@ -157,6 +157,13 @@ class Chef
|
|||
:long => "--udp-endpoints PORT_LIST",
|
||||
:description => "Comma separated list of UDP local and public ports to open i.e. '80:80,433:5000'"
|
||||
|
||||
option :identity_file,
|
||||
:long => "--identity-file FILENAME",
|
||||
:description => "SSH key path, optional. It is the RSA private key. Specify either ssh-password or identity-file"
|
||||
|
||||
option :identity_file_passphrase,
|
||||
:long => "--identity-file-passphrase PASSWORD",
|
||||
:description => "SSH key passphrase. Optional, specify if passphrase for identity-file exists"
|
||||
|
||||
def strip_non_ascii(string)
|
||||
string.gsub(/[^0-9a-z ]/i, '')
|
||||
|
@ -241,9 +248,6 @@ class Chef
|
|||
end
|
||||
puts ui.list(details, :columns_across, 4)
|
||||
end
|
||||
def is_platform_windows?
|
||||
return RUBY_PLATFORM.scan('w32').size > 0
|
||||
end
|
||||
|
||||
def run
|
||||
$stdout.sync = true
|
||||
|
@ -268,22 +272,13 @@ class Chef
|
|||
config[:storage_account] = storage.name.to_s
|
||||
end
|
||||
end
|
||||
if is_image_windows?
|
||||
if is_platform_windows?
|
||||
#require 'em-winrs'
|
||||
else
|
||||
require 'gssapi'
|
||||
require 'winrm'
|
||||
require 'em-winrm'
|
||||
end
|
||||
end
|
||||
|
||||
server = connection.deploys.create(create_server_def)
|
||||
fqdn = server.publicipaddress
|
||||
|
||||
puts("\n")
|
||||
if is_image_windows?
|
||||
if locate_config_value(:bootstrap_protocol) == 'ssh'
|
||||
fqdn = server.sshipaddress
|
||||
port = server.sshport
|
||||
print "\n#{ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)}"
|
||||
|
||||
|
@ -293,7 +288,6 @@ class Chef
|
|||
}
|
||||
|
||||
elsif locate_config_value(:bootstrap_protocol) == 'winrm'
|
||||
fqdn = server.winrmipaddress
|
||||
port = server.winrmport
|
||||
|
||||
print "\n#{ui.color("Waiting for winrm on #{fqdn}:#{port}", :magenta)}"
|
||||
|
@ -307,12 +301,11 @@ class Chef
|
|||
sleep 15
|
||||
bootstrap_for_windows_node(server,fqdn).run
|
||||
else
|
||||
unless server && server.sshipaddress && server.sshport
|
||||
unless server && server.publicipaddress && server.sshport
|
||||
Chef::Log.fatal("server not created")
|
||||
exit 1
|
||||
end
|
||||
|
||||
fqdn = server.sshipaddress
|
||||
port = server.sshport
|
||||
|
||||
print "\n#{ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)}"
|
||||
|
@ -340,13 +333,12 @@ class Chef
|
|||
|
||||
def bootstrap_for_windows_node(server, fqdn)
|
||||
if locate_config_value(:bootstrap_protocol) == 'winrm'
|
||||
if is_platform_windows?
|
||||
#require 'em-winrs'
|
||||
else
|
||||
|
||||
load_winrm_deps
|
||||
if not Chef::Platform.windows?
|
||||
require 'gssapi'
|
||||
require 'winrm'
|
||||
require 'em-winrm'
|
||||
end
|
||||
|
||||
bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
|
||||
|
||||
bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || 'Administrator'
|
||||
|
@ -386,6 +378,7 @@ class Chef
|
|||
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
||||
bootstrap.config[:distro] = locate_config_value(:distro)
|
||||
bootstrap.config[:use_sudo] = true unless locate_config_value(:ssh_user) == 'root'
|
||||
bootstrap.config[:use_sudo_password] = true if bootstrap.config[:use_sudo]
|
||||
bootstrap.config[:template_file] = config[:template_file]
|
||||
bootstrap.config[:environment] = locate_config_value(:environment)
|
||||
# may be needed for vpc_mode
|
||||
|
@ -417,7 +410,7 @@ class Chef
|
|||
:role_size => locate_config_value(:role_size),
|
||||
:tcp_endpoints => locate_config_value(:tcp_endpoints),
|
||||
:udp_endpoints => locate_config_value(:udp_endpoints),
|
||||
:bootstrap_proto => locate_config_value(:bootstrap_protocol)
|
||||
:bootstrap_proto => locate_config_value(:bootstrap_protocol)
|
||||
}
|
||||
|
||||
if is_image_windows?
|
||||
|
@ -430,12 +423,19 @@ class Chef
|
|||
else
|
||||
server_def[:os_type] = 'Linux'
|
||||
server_def[:bootstrap_proto] = 'ssh'
|
||||
if not locate_config_value(:ssh_user) or not locate_config_value(:ssh_password)
|
||||
ui.error("SSH User and SSH Password are compulsory parameters")
|
||||
if not locate_config_value(:ssh_user)
|
||||
ui.error("SSH User is compulsory parameter")
|
||||
exit 1
|
||||
end
|
||||
unless locate_config_value(:ssh_password) or locate_config_value(:identity_file)
|
||||
ui.error("Specify either SSH Key or SSH Password")
|
||||
exit 1
|
||||
end
|
||||
|
||||
server_def[:ssh_user] = locate_config_value(:ssh_user)
|
||||
server_def[:ssh_password] = locate_config_value(:ssh_password)
|
||||
server_def[:identity_file] = locate_config_value(:identity_file)
|
||||
server_def[:identity_file_passphrase] = locate_config_value(:identity_file_passphrase)
|
||||
end
|
||||
server_def
|
||||
end
|
||||
|
|
|
@ -32,11 +32,11 @@ class Chef
|
|||
|
||||
banner "knife azure server delete SERVER [SERVER] (options)"
|
||||
|
||||
option :purge_os_disk,
|
||||
:long => "--purge-os-disk",
|
||||
option :preserve_os_disk,
|
||||
:long => "--preserve-os-disk",
|
||||
:boolean => true,
|
||||
:default => true,
|
||||
:description => "Destroy corresponding OS Disk"
|
||||
:default => false,
|
||||
:description => "Preserve corresponding OS Disk"
|
||||
|
||||
option :purge,
|
||||
:short => "-P",
|
||||
|
@ -50,6 +50,16 @@ class Chef
|
|||
:long => "--node-name NAME",
|
||||
:description => "The name of the node and client to delete, if it differs from the server name. Only has meaning when used with the '--purge' option."
|
||||
|
||||
option :preserve_hosted_service,
|
||||
:long => "--preserve-hosted-service",
|
||||
:boolean => true,
|
||||
:default => false,
|
||||
:description => "Dont destroy corresponding hosted service. If the option is not set, it deletes the service not used by any VMs."
|
||||
|
||||
option :azure_hosted_service_name,
|
||||
:long => "--azure-dns-name NAME",
|
||||
:description => "specifies the DNS name (also known as hosted service name)"
|
||||
|
||||
# Extracted from Chef::Knife.delete_object, because it has a
|
||||
# confirmation step built in... By specifying the '--purge'
|
||||
# flag (and also explicitly confirming the server destruction!)
|
||||
|
@ -72,7 +82,7 @@ class Chef
|
|||
@name_args.each do |name|
|
||||
|
||||
begin
|
||||
server = connection.roles.find(name)
|
||||
server = connection.roles.find(name, params = { :azure_hosted_service_name => locate_config_value(:azure_hosted_service_name) })
|
||||
if not server
|
||||
ui.warn("Server #{name} does not exist")
|
||||
return
|
||||
|
@ -82,13 +92,13 @@ class Chef
|
|||
msg_pair('Deployment', server.deployname)
|
||||
msg_pair('Role', server.name)
|
||||
msg_pair('Size', server.size)
|
||||
msg_pair('SSH Ip Address', server.sshipaddress)
|
||||
msg_pair('SSH Port', server.sshport)
|
||||
msg_pair('Public Ip Address', server.publicipaddress)
|
||||
|
||||
puts "\n"
|
||||
confirm("Do you really want to delete this server")
|
||||
|
||||
connection.roles.delete(name, params = { :purge_os_disk => locate_config_value(:purge_os_disk) })
|
||||
connection.roles.delete(name, params = { :preserve_os_disk => locate_config_value(:preserve_os_disk),
|
||||
:preserve_hosted_service => locate_config_value(:preserve_hosted_service),
|
||||
:hostedservicename => server.hostedservicename })
|
||||
|
||||
puts "\n"
|
||||
ui.warn("Deleted server #{server.name}")
|
||||
|
|
|
@ -36,7 +36,7 @@ class Chef
|
|||
server_labels = ['DNS Name', 'VM Name', 'Status', 'IP Address', 'SSH Port', 'WinRM Port' ]
|
||||
server_list = server_labels.map {|label| ui.color(label, :bold)}
|
||||
items = connection.roles.all
|
||||
|
||||
|
||||
items.each do |server|
|
||||
server_list << server.hostedservicename.to_s+".cloudapp.net" # Info about the DNS name at http://msdn.microsoft.com/en-us/library/ee460806.aspx
|
||||
server_list << server.name.to_s
|
||||
|
@ -50,8 +50,8 @@ class Chef
|
|||
else
|
||||
ui.color('ready', :green)
|
||||
end
|
||||
end
|
||||
server_list << server.sshipaddress.to_s
|
||||
end
|
||||
server_list << server.publicipaddress.to_s
|
||||
server_list << server.sshport.to_s
|
||||
server_list << server.winrmport.to_s
|
||||
end
|
||||
|
|
|
@ -15,6 +15,10 @@ require 'chef/knife/azure_server_create'
|
|||
require 'chef/knife/azure_server_describe'
|
||||
require 'chef/knife/azure_image_list'
|
||||
|
||||
require 'fileutils'
|
||||
#Create an empty mock certificate file
|
||||
FileUtils.touch('AzureLinuxCert.pem')
|
||||
|
||||
def tmpFile filename
|
||||
tmpdir = 'tmp'
|
||||
Dir::mkdir tmpdir unless FileTest::directory?(tmpdir)
|
||||
|
|
|
@ -168,6 +168,52 @@ describe "for bootstrap protocol ssh:" do
|
|||
@bootstrap.should_receive(:run)
|
||||
@server_instance.run
|
||||
end
|
||||
|
||||
context "ssh key" do
|
||||
before do
|
||||
Chef::Config[:knife][:ssh_password] = ''
|
||||
Chef::Config[:knife][:identity_file] = 'ssh_key'
|
||||
end
|
||||
it "check if ssh-key set correctly" do
|
||||
@server_instance.should_receive(:is_image_windows?).and_return(false)
|
||||
@server_params = @server_instance.create_server_def
|
||||
@server_params[:os_type].should == 'Linux'
|
||||
@server_params[:identity_file].should == 'ssh_key'
|
||||
@server_params[:ssh_user].should == 'ssh_user'
|
||||
@server_params[:bootstrap_proto].should == 'ssh'
|
||||
@server_params[:hosted_service_name].should == 'service001'
|
||||
end
|
||||
it "successful bootstrap with ssh key" do
|
||||
@server_instance.should_receive(:is_image_windows?).exactly(3).times.and_return(false)
|
||||
@bootstrap = Chef::Knife::Bootstrap.new
|
||||
Chef::Knife::Bootstrap.stub(:new).and_return(@bootstrap)
|
||||
@bootstrap.should_receive(:run)
|
||||
@server_instance.connection.certificates.stub(:generate_public_key_certificate_data).and_return("cert_data")
|
||||
@server_instance.connection.certificates.should_receive(:create)
|
||||
@server_instance.run
|
||||
end
|
||||
end
|
||||
|
||||
context "bootstrap"
|
||||
before do
|
||||
@server_params = @server_instance.create_server_def
|
||||
@bootstrap = Chef::Knife::Bootstrap.new
|
||||
Chef::Knife::Bootstrap.stub(:new).and_return(@bootstrap)
|
||||
end
|
||||
|
||||
it "enables sudo password when ssh_user is not root" do
|
||||
@bootstrap.should_receive(:run)
|
||||
@server_instance.run
|
||||
@bootstrap.config[:use_sudo_password].should == true
|
||||
end
|
||||
|
||||
it "does not enable sudo password when ssh_user is root" do
|
||||
@bootstrap.should_receive(:run)
|
||||
Chef::Config[:knife][:ssh_user] = 'root'
|
||||
@server_instance.run
|
||||
@bootstrap.config[:use_sudo_password].should_not == true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../unit/query_azure_mock')
|
||||
|
||||
describe Chef::Knife::AzureServerDelete do
|
||||
include AzureSpecHelper
|
||||
include QueryAzureMock
|
||||
|
||||
before do
|
||||
@server_instance = Chef::Knife::AzureServerDelete.new
|
||||
|
||||
{
|
||||
:azure_subscription_id => 'azure_subscription_id',
|
||||
:azure_mgmt_cert => 'AzureLinuxCert.pem',
|
||||
:azure_host_name => 'preview.core.windows-int.net',
|
||||
:role_name => 'vm01',
|
||||
:service_location => 'service_location',
|
||||
:source_image => 'source_image',
|
||||
:role_size => 'role_size',
|
||||
:hosted_service_name => 'service001',
|
||||
:storage_account => 'ka001testeurope'
|
||||
}.each do |key, value|
|
||||
Chef::Config[:knife][key] = value
|
||||
end
|
||||
|
||||
stub_query_azure (@server_instance.connection)
|
||||
|
||||
@server_instance.stub(:confirm).and_return(:true)
|
||||
|
||||
@server_instance.stub(:puts)
|
||||
@server_instance.stub(:print)
|
||||
@server_instance.ui.stub(:warn)
|
||||
@server_instance.ui.should_not_receive(:error).and_call_original
|
||||
end
|
||||
|
||||
it "server delete test" do
|
||||
@server_instance.name_args = ['vm01']
|
||||
@server_instance.ui.should_receive(:warn).twice
|
||||
@server_instance.connection.roles.should_receive(:delete).and_call_original
|
||||
@server_instance.run
|
||||
end
|
||||
|
||||
it "test hosted service cleanup with shared service" do
|
||||
@server_instance.name_args = ['role001']
|
||||
@server_instance.ui.should_receive(:warn).twice
|
||||
@server_instance.connection.roles.should_receive(:delete).and_call_original
|
||||
@server_instance.connection.hosts.should_not_receive(:delete)
|
||||
@server_instance.run
|
||||
end
|
||||
|
||||
it "dont cleanup hosted service when --preserve-hosted-service param set" do
|
||||
@server_instance.name_args = ['vm01']
|
||||
Chef::Config[:knife][:preserve_hosted_service] = true
|
||||
@server_instance.ui.should_receive(:warn).twice
|
||||
@server_instance.connection.roles.should_receive(:delete).and_call_original
|
||||
@server_instance.connection.hosts.should_not_receive(:delete)
|
||||
@server_instance.run
|
||||
end
|
||||
|
||||
it "delete vm within a hosted service when --azure-dns-name param set" do
|
||||
test_hostname = 'vm002'
|
||||
@server_instance.name_args = [test_hostname]
|
||||
|
||||
Chef::Config[:knife][:azure_hosted_service_name] = 'service001'
|
||||
Chef::Config[:knife][:preserve_os_disk] = true
|
||||
|
||||
@server_instance.connection.roles.should_receive(:delete).and_call_original
|
||||
|
||||
# test correct params are passed to azure API.
|
||||
@server_instance.connection.should_receive(:query_azure).with("hostedservices/#{Chef::Config[:knife][:azure_hosted_service_name]}/deployments/deployment001/roles/#{test_hostname}", "delete")
|
||||
|
||||
@server_instance.run
|
||||
end
|
||||
|
||||
it "delete multiple vm's within a hosted service when --azure-dns-name param set" do
|
||||
test_hostnames = ['vm002', 'role002', 'role001']
|
||||
@server_instance.name_args = test_hostnames
|
||||
|
||||
Chef::Config[:knife][:azure_hosted_service_name] = 'service001'
|
||||
Chef::Config[:knife][:preserve_os_disk] = true
|
||||
|
||||
@server_instance.connection.roles.should_receive(:delete).exactly(3).times.and_call_original
|
||||
|
||||
# test correct calls are made to azure API.
|
||||
@server_instance.connection.should_receive(:query_azure).with("hostedservices/#{Chef::Config[:knife][:azure_hosted_service_name]}/deployments/deployment001/roles/#{test_hostnames[0]}", "delete")
|
||||
@server_instance.connection.should_receive(:query_azure).with("hostedservices/#{Chef::Config[:knife][:azure_hosted_service_name]}/deployments/deployment001/roles/#{test_hostnames[1]}", "delete")
|
||||
@server_instance.connection.should_receive(:query_azure).with("hostedservices/#{Chef::Config[:knife][:azure_hosted_service_name]}/deployments/deployment001", "delete")
|
||||
|
||||
@server_instance.run
|
||||
end
|
||||
end
|
|
@ -27,7 +27,7 @@ include AzureSpecHelper
|
|||
role.size.should_not be_nil
|
||||
role.ipaddress.should_not be_nil
|
||||
role.sshport.should_not be_nil
|
||||
role.sshipaddress.should_not be_nil
|
||||
role.publicipaddress.should_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -41,7 +41,7 @@ include AzureSpecHelper
|
|||
puts 'size: ' + role.size
|
||||
puts 'ip address: ' + role.ipaddress
|
||||
puts 'ssh port: ' + role.sshport
|
||||
puts 'ssh ip address: ' + role.sshipaddress
|
||||
puts 'ssh ip address: ' + role.publicipaddress
|
||||
role.tcpports.each do |port|
|
||||
puts ' tcp: ' + port['Name'] + ' ' + port['Vip'] + ' ' +
|
||||
port['PublicPort'] + ' ' + port['LocalPort']
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
module QueryAzureMock
|
||||
def setup_query_azure_mock
|
||||
create_connection
|
||||
|
@ -36,8 +35,12 @@ module QueryAzureMock
|
|||
retval = Nokogiri::XML readFile('list_hosts.xml')
|
||||
elsif name == 'hostedservices/service001/deploymentslots/Production'
|
||||
retval = Nokogiri::XML readFile('list_deployments_for_service001.xml')
|
||||
elsif name == 'hostedservices/service001/deployments/deployment001/roles/role001'
|
||||
retval = Nokogiri::XML readFile('list_deployments_for_service001.xml')
|
||||
elsif name == 'hostedservices/service002/deploymentslots/Production'
|
||||
retval = Nokogiri::XML readFile('list_deployments_for_service002.xml')
|
||||
elsif name == 'hostedservices/service002/deployments/testrequest'
|
||||
retval = Nokogiri::XML readFile('list_deployments_for_service002.xml')
|
||||
elsif name == 'hostedservices/service003/deploymentslots/Production'
|
||||
retval = Nokogiri::XML readFile('list_deployments_for_service003.xml')
|
||||
elsif name == 'storageservices'
|
||||
|
|
|
@ -10,7 +10,7 @@ describe "roles" do
|
|||
context 'delete a role' do
|
||||
context 'when the role is not the only one in a deployment' do
|
||||
it 'should pass in correct name, verb, and body' do
|
||||
@connection.roles.delete('vm002', {:purge_os_disk => false});
|
||||
@connection.roles.delete('vm002', {:preserve_os_disk => true});
|
||||
@deletename.should == 'hostedservices/service001/deployments/deployment001/roles/vm002'
|
||||
@deleteverb.should == 'delete'
|
||||
@deletebody.should == nil
|
||||
|
@ -20,7 +20,7 @@ describe "roles" do
|
|||
context 'delete a role' do
|
||||
context 'when the role is the only one in a deployment' do
|
||||
it 'should pass in correct name, verb, and body' do
|
||||
@connection.roles.delete('vm01', {:purge_os_disk => false});
|
||||
@connection.roles.delete('vm01', {:preserve_os_disk => true});
|
||||
@deletename.should == 'hostedservices/service002/deployments/testrequest'
|
||||
@deleteverb.should == 'delete'
|
||||
@deletebody.should == nil
|
||||
|
|
|
@ -27,6 +27,6 @@ describe "roles" do
|
|||
role.size.should_not be_nil
|
||||
role.ipaddress.should_not be_nil
|
||||
role.sshport.should_not be_nil
|
||||
role.sshipaddress.should_not be_nil
|
||||
role.publicipaddress.should_not be_nil
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче