From 739d58405415836a43c183fd86e73f8997b79b04 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 28 Apr 2021 22:02:05 +0000 Subject: [PATCH 01/10] Upgrade to GitHub-native Dependabot --- .github/dependabot.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..a78aa4d3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: +- package-ecosystem: bundler + directory: "/" + schedule: + interval: daily + time: "06:00" + timezone: America/Los_Angeles + open-pull-requests-limit: 10 + ignore: + - dependency-name: activesupport + versions: + - 6.1.1 + - 6.1.2 + - 6.1.2.1 + - 6.1.3 From 19a1168184ed8a3aaad6efa971dc6ba6f35efa28 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Wed, 2 Jun 2021 19:21:58 +0000 Subject: [PATCH 02/10] Bump version to 3.0.8 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- lib/knife-azure/version.rb | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89e9a9a5..f3a057be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # knife-azure Change Log - -## [v3.0.7](https://github.com/chef/knife-azure/tree/v3.0.7) (2020-09-10) + +## [v3.0.8](https://github.com/chef/knife-azure/tree/v3.0.8) (2021-06-02) #### Merged Pull Requests -- Update activesupport requirement from 6.0.3.1 to 6.0.3.3 [#536](https://github.com/chef/knife-azure/pull/536) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) +- Upgrade to GitHub-native Dependabot [#544](https://github.com/chef/knife-azure/pull/544) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) ### Changes not yet released to rubygems.org #### Merged Pull Requests +- Upgrade to GitHub-native Dependabot [#544](https://github.com/chef/knife-azure/pull/544) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) - Update activesupport requirement from 6.0.3.1 to 6.0.3.3 [#536](https://github.com/chef/knife-azure/pull/536) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) diff --git a/VERSION b/VERSION index 3a8b9717..e46454be 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0.7 \ No newline at end of file +3.0.8 \ No newline at end of file diff --git a/lib/knife-azure/version.rb b/lib/knife-azure/version.rb index 6c38cb0d..abc99191 100755 --- a/lib/knife-azure/version.rb +++ b/lib/knife-azure/version.rb @@ -17,7 +17,7 @@ module Knife module Azure - VERSION = "3.0.7".freeze + VERSION = "3.0.8".freeze MAJOR, MINOR, TINY = VERSION.split(".") end end From dc3859c5348c46414d58ed138aeeb2e1c8f30332 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 2 Jun 2021 14:32:04 -0700 Subject: [PATCH 03/10] Remove the legacy azure commands that use the new deprecated API The ASM API was deprecated in March of 2018. These commands are legacy / broken and need to go away at this point. Signed-off-by: Tim Smith --- .expeditor/verify.pipeline.yml | 8 +- .rubocop.yml | 78 +- Gemfile | 16 - README.md | 14 +- RELEASE_NOTES.md | 52 - Rakefile | 16 +- docs/ASM.md | 337 ---- docs/bootstrap.md | 12 - docs/configuration.md | 28 - knife-azure.gemspec | 8 +- lib/azure/custom_errors.rb | 2 +- .../ARM_deployment_template.rb | 10 +- .../resource_management/ARM_interface.rb | 10 +- .../windows_credentials.rb | 4 +- lib/azure/service_management/ASM_interface.rb | 310 ---- lib/azure/service_management/ag.rb | 99 -- lib/azure/service_management/certificate.rb | 235 --- lib/azure/service_management/connection.rb | 102 -- lib/azure/service_management/deploy.rb | 221 --- lib/azure/service_management/disk.rb | 68 - lib/azure/service_management/host.rb | 184 --- lib/azure/service_management/image.rb | 94 -- lib/azure/service_management/loadbalancer.rb | 78 - lib/azure/service_management/rest.rb | 126 -- lib/azure/service_management/role.rb | 717 --------- .../service_management/storageaccount.rb | 127 -- lib/azure/service_management/utility.rb | 40 - lib/azure/service_management/vnet.rb | 134 -- lib/chef/knife/azure_ag_create.rb | 73 - lib/chef/knife/azure_ag_list.rb | 35 - lib/chef/knife/azure_image_list.rb | 56 - lib/chef/knife/azure_internal-lb_create.rb | 74 - lib/chef/knife/azure_internal-lb_list.rb | 35 - lib/chef/knife/azure_server_create.rb | 531 ------- lib/chef/knife/azure_server_delete.rb | 136 -- lib/chef/knife/azure_server_list.rb | 38 - lib/chef/knife/azure_server_show.rb | 41 - lib/chef/knife/azure_vnet_create.rb | 74 - lib/chef/knife/azure_vnet_list.rb | 35 - lib/chef/knife/azurerm_server_create.rb | 2 +- lib/chef/knife/bootstrap/bootstrapper.rb | 15 +- lib/chef/knife/bootstrap_azure.rb | 191 --- lib/chef/knife/helpers/azure_base.rb | 392 ----- lib/chef/knife/helpers/azurerm_base.rb | 4 +- spec/spec_helper.rb | 32 +- spec/unit/ags_spec.rb | 72 - spec/unit/azure_ag_create_spec.rb | 52 - spec/unit/azure_ag_list_spec.rb | 57 - spec/unit/azure_base_spec.rb | 248 --- spec/unit/azure_image_list_spec.rb | 93 -- spec/unit/azure_internal-lb_create_spec.rb | 54 - spec/unit/azure_internal-lb_list_spec.rb | 55 - spec/unit/azure_server_create_spec.rb | 1264 --------------- spec/unit/azure_server_delete_spec.rb | 256 --- spec/unit/azure_server_list_spec.rb | 57 - spec/unit/azure_server_show_spec.rb | 140 -- spec/unit/azure_vnet_create_spec.rb | 54 - spec/unit/azure_vnet_list_spec.rb | 41 - spec/unit/azurerm_base_spec.rb | 2 +- spec/unit/azurerm_server_create_spec.rb | 1 - spec/unit/azurerm_server_delete_spec.rb | 6 +- spec/unit/bootstrap_azure_spec.rb | 791 ---------- spec/unit/bootstrap_azurerm_spec.rb | 1 - spec/unit/certificate_spec.rb | 49 - spec/unit/deploys_list_spec.rb | 85 - spec/unit/disks_spec.rb | 71 - spec/unit/hosts_spec.rb | 90 -- spec/unit/images_spec.rb | 62 - spec/unit/query_azure_mock.rb | 6 - spec/unit/roles_create_spec.rb | 217 --- spec/unit/roles_list_spec.rb | 59 - spec/unit/storageaccount_spec.rb | 86 - spec/unit/vnet_config_spec.rb | 1393 ----------------- spec/unit/vnet_spec.rb | 88 -- spec/unit/windows_credentials_spec.rb | 2 - 75 files changed, 53 insertions(+), 10293 deletions(-) delete mode 100644 RELEASE_NOTES.md delete mode 100644 docs/ASM.md delete mode 100644 lib/azure/service_management/ASM_interface.rb delete mode 100644 lib/azure/service_management/ag.rb delete mode 100755 lib/azure/service_management/certificate.rb delete mode 100644 lib/azure/service_management/connection.rb delete mode 100755 lib/azure/service_management/deploy.rb delete mode 100755 lib/azure/service_management/disk.rb delete mode 100755 lib/azure/service_management/host.rb delete mode 100755 lib/azure/service_management/image.rb delete mode 100644 lib/azure/service_management/loadbalancer.rb delete mode 100755 lib/azure/service_management/rest.rb delete mode 100644 lib/azure/service_management/role.rb delete mode 100644 lib/azure/service_management/storageaccount.rb delete mode 100755 lib/azure/service_management/utility.rb delete mode 100644 lib/azure/service_management/vnet.rb delete mode 100644 lib/chef/knife/azure_ag_create.rb delete mode 100644 lib/chef/knife/azure_ag_list.rb delete mode 100755 lib/chef/knife/azure_image_list.rb delete mode 100644 lib/chef/knife/azure_internal-lb_create.rb delete mode 100644 lib/chef/knife/azure_internal-lb_list.rb delete mode 100644 lib/chef/knife/azure_server_create.rb delete mode 100755 lib/chef/knife/azure_server_delete.rb delete mode 100755 lib/chef/knife/azure_server_list.rb delete mode 100755 lib/chef/knife/azure_server_show.rb delete mode 100644 lib/chef/knife/azure_vnet_create.rb delete mode 100644 lib/chef/knife/azure_vnet_list.rb delete mode 100644 lib/chef/knife/bootstrap_azure.rb delete mode 100644 lib/chef/knife/helpers/azure_base.rb delete mode 100644 spec/unit/ags_spec.rb delete mode 100644 spec/unit/azure_ag_create_spec.rb delete mode 100644 spec/unit/azure_ag_list_spec.rb delete mode 100644 spec/unit/azure_base_spec.rb delete mode 100644 spec/unit/azure_image_list_spec.rb delete mode 100644 spec/unit/azure_internal-lb_create_spec.rb delete mode 100644 spec/unit/azure_internal-lb_list_spec.rb delete mode 100644 spec/unit/azure_server_create_spec.rb delete mode 100644 spec/unit/azure_server_delete_spec.rb delete mode 100644 spec/unit/azure_server_list_spec.rb delete mode 100644 spec/unit/azure_server_show_spec.rb delete mode 100644 spec/unit/azure_vnet_create_spec.rb delete mode 100644 spec/unit/azure_vnet_list_spec.rb delete mode 100644 spec/unit/bootstrap_azure_spec.rb delete mode 100644 spec/unit/certificate_spec.rb delete mode 100755 spec/unit/deploys_list_spec.rb delete mode 100755 spec/unit/disks_spec.rb delete mode 100755 spec/unit/hosts_spec.rb delete mode 100755 spec/unit/images_spec.rb mode change 100755 => 100644 spec/unit/query_azure_mock.rb delete mode 100755 spec/unit/roles_create_spec.rb delete mode 100755 spec/unit/roles_list_spec.rb delete mode 100644 spec/unit/storageaccount_spec.rb delete mode 100644 spec/unit/vnet_config_spec.rb delete mode 100644 spec/unit/vnet_spec.rb diff --git a/.expeditor/verify.pipeline.yml b/.expeditor/verify.pipeline.yml index eb5c46df..2bf9cfaa 100644 --- a/.expeditor/verify.pipeline.yml +++ b/.expeditor/verify.pipeline.yml @@ -11,13 +11,6 @@ expeditor: steps: -- label: run-specs-ruby-2.6 - command: - - .expeditor/run_linux_tests.sh rake - expeditor: - executor: - docker: - image: ruby:2.6-buster - label: run-specs-ruby-2.7 command: - .expeditor/run_linux_tests.sh rake @@ -34,3 +27,4 @@ steps: executor: docker: host_os: windows + image: rubydistros/windows-2019:2.7 diff --git a/.rubocop.yml b/.rubocop.yml index 9ca37654..2d950ab8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,79 +1,25 @@ -Layout/DefEndAlignment: - Exclude: - - 'lib/azure/custom_errors.rb' - - 'lib/azure/service_management/ASM_interface.rb' - -Layout/EndAlignment: - Exclude: - - 'lib/azure/resource_management/ARM_interface.rb' - - 'lib/azure/resource_management/windows_credentials.rb' - - 'lib/azure/service_management/role.rb' - - 'lib/chef/knife/azure_server_delete.rb' - - 'spec/unit/azure_base_spec.rb' - -Lint/AmbiguousBlockAssociation: - Exclude: - - 'lib/azure/service_management/rest.rb' - -Lint/AmbiguousRegexpLiteral: - Exclude: - - 'lib/chef/knife/helpers/azurerm_base.rb' - -Lint/DuplicateMethods: - Exclude: - - 'lib/azure/service_management/storageaccount.rb' +Naming/VariableName: + Enabled: false +# Offense count: 2 Lint/Loop: Exclude: - 'lib/azure/resource_management/ARM_interface.rb' - - 'lib/azure/service_management/certificate.rb' - - 'lib/chef/knife/azure_server_create.rb' - -Lint/ParenthesesAsGroupedExpression: - Exclude: - - 'lib/azure/resource_management/ARM_deployment_template.rb' - - 'lib/azure/service_management/certificate.rb' - - 'lib/azure/service_management/deploy.rb' - - 'spec/unit/ags_spec.rb' - - 'spec/unit/azurerm_base_spec.rb' - - 'spec/unit/azurerm_server_delete_spec.rb' - - 'spec/unit/deploys_list_spec.rb' - - 'spec/unit/disks_spec.rb' - - 'spec/unit/hosts_spec.rb' - - 'spec/unit/roles_list_spec.rb' - - 'spec/unit/storageaccount_spec.rb' - - 'spec/unit/vnet_spec.rb' +# Offense count: 14 Lint/UselessAssignment: - Enabled: false - -Naming/VariableName: Exclude: - 'lib/azure/resource_management/ARM_deployment_template.rb' - - 'lib/azure/service_management/deploy.rb' - - 'lib/azure/service_management/host.rb' - - 'lib/azure/service_management/loadbalancer.rb' - - 'lib/azure/service_management/role.rb' - - 'lib/azure/service_management/storageaccount.rb' - - 'spec/functional/host_test.rb' - - 'spec/unit/images_spec.rb' - - 'spec/unit/query_azure_mock.rb' - -Style/For: - Exclude: - - 'lib/azure/resource_management/ARM_deployment_template.rb' - - 'lib/azure/service_management/certificate.rb' - - 'lib/azure/service_management/role.rb' - -Style/NonNilCheck: - Exclude: - - 'lib/azure/service_management/deploy.rb' - - 'lib/chef/knife/azure_server_create.rb' - - 'lib/chef/knife/bootstrap_azure.rb' - - 'lib/chef/knife/helpers/azure_base.rb' + - 'lib/azure/resource_management/ARM_interface.rb' + - 'lib/azure/resource_management/windows_credentials.rb' + - 'lib/chef/knife/azurerm_server_create.rb' + - 'lib/chef/knife/azurerm_server_delete.rb' + - 'lib/chef/knife/helpers/azurerm_base.rb' + - 'spec/unit/azurerm_server_delete_spec.rb' + - 'spec/unit/azurerm_server_show_spec.rb' +# Offense count: 1 Style/NumericPredicate: Exclude: - 'spec/**/*' - - 'lib/azure/service_management/role.rb' - 'lib/chef/knife/helpers/azurerm_base.rb' diff --git a/Gemfile b/Gemfile index 27b34788..fa77a084 100644 --- a/Gemfile +++ b/Gemfile @@ -2,17 +2,7 @@ source "https://rubygems.org" gemspec -group :debug do - gem "pry" - gem "pry-byebug" - gem "pry-stack_explorer" -end - group :test do - # until we remove support for Ruby 2.5 - if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6") - gem "chef-zero", "< 15.0" - end gem "activesupport", "6.0.3.3" gem "chefstyle" gem "equivalent-xml", "~> 0.6.0" @@ -25,9 +15,3 @@ group :test do gem "rspec", ">= 3.0" gem "rspec_junit_formatter" end - -group :docs do - gem "github-markup" - gem "redcarpet" - gem "yard" -end diff --git a/README.md b/README.md index fa43f6f5..324740cc 100644 --- a/README.md +++ b/README.md @@ -27,29 +27,21 @@ https://downloads.chef.io/chef-workstation/ ## Modes -`knife-azure 1.6.0` onwards supports Azure Resource Manager (preferred). You can easily switch between: - -- Resource manager: commands using the Azure Resource Manager API -- Service management: commands using the Azure service management API - -They are not designed to work together. Commands starting with `knife azurerm` use ARM mode, while commands starting with `knife azure` use ASM mode. +`knife-azure 4.0` onwards removes the legacy `knife azure` commands that utilized the Azure Service Management API. This API was deprecated in March 2018. ## Configuration 1. [ARM Configuration](docs/configuration.md#arm-mode) -1. [ASM Configuration](docs/configuration.md#asm-mode) ## Detailed Usage 1. [ARM Mode](docs/ARM.md) -1. [ASM Mode](docs/ASM.md) -## Bootstrap existing VM to install the chef-client using chef-extension +## Bootstrap existing VM to install the Chef Infra Client using chef-extension -We have added a utility in ARM and ASM to bootstrap existing VM. This will install the chef-client using chef extension on your VM. +We have added a utility to bootstrap existing VM. This will install the Chef Infra Client using chef extension on your VM. 1. [Bootstrap Doc for ARM Mode](docs/bootstrap.md#arm-mode) -1. [Bootstrap Doc for ASM Mode](docs/bootstrap.md#asm-mode) ## Contributing diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md deleted file mode 100644 index 0942937d..00000000 --- a/RELEASE_NOTES.md +++ /dev/null @@ -1,52 +0,0 @@ - - -# knife-azure 1.8.0 release notes: -In this release, The --chef-service-interval option has been renamed to --chef-daemon-interval. - -Now supports the latest azure-sdk gem, version 0.9.0. - -New options introduced: - -`--azure_availability_set`, which allows the user to create virtual machines in specified availability set. - -`--daemon`, which allows the user to configure the chef-client to run as a daemon by specifying `none`, `service`, or `task`. This option is only supported on Windows and requires the `--bootstrap-protocol` option to be set to `cloud-api`. - -Please file bugs or feature requests against the [KNIFE_AZURE](https://github.com/chef/knife-azure/issues) repository. -More information on the contribution process for Chef projects can be found in the [Chef Contributions document](https://docs.chef.io/community_contributions.html). - -## knife-azure on RubyGems and Github -https://rubygems.org/gems/knife-azure - -https://github.com/chef/knife-azure - -## Features added in this release: - -See the [1.8.0 CHANGELOG](https://github.com/chef/knife-azure/blob/1.8.0/CHANGELOG.md) for the complete list of features added in this release. - -Here is a partial list: - -* Added `--azure_availability_set` option which allows the user to create virtual machine in specified availability set. [\#453](https://github.com/chef/knife-azure/pull/453) ([piyushawasthi](https://github.com/piyushawasthi)) - -* Added `--daemon` option for chef extension. [\#417](https://github.com/chef/knife-azure/pull/417) ([Vasu1105](https://github.com/Vasu1105)) - -## Issues fixed in this release: - -See the [1.8.0 CHANGELOG](https://github.com/chef/knife-azure/blob/1.8.0/CHANGELOG.md) for the complete list of issues fixed in this release. - -Here is a partial list: - -* Master is broken for any azurerm server create operations that involve virtual networks [\#460](https://github.com/chef/knife-azure/pull/460) ([harikesh-kolekar](https://github.com/harikesh-kolekar)) -* Running `azurerm server create` with Chef Environment argument does not correctly assign environment to the created node [\#456](https://github.com/chef/knife-azure/pull/456) ([dheerajd-msys](https://github.com/dheerajd-msys)) -* Fix for `--delete-resource-group` works, but exits as failed [\#459](https://github.com/chef/knife-azure/pull/459) ([dheerajd-msys](https://github.com/dheerajd-msys)) -* Can not create vms in required availability set (ARM) [\#453](https://github.com/chef/knife-azure/pull/453) ([piyushawasthi](https://github.com/piyushawasthi)) -* Fix for azurerm command bootstrap was not happening fully [\#447](https://github.com/chef/knife-azure/pull/447) ([harikesh-kolekar](https://github.com/harikesh-kolekar)) -* knife-azure does not work with latest Chefdk [\#445](https://github.com/chef/knife-azure/pull/445) ([harikesh-kolekar](https://github.com/harikesh-kolekar)) -* Fix for `--node-ssl-verify-mode none` does not write appropriate value to resulting client.rb [\#437](https://github.com/chef/knife-azure/pull/437) ([piyushawasthi](https://github.com/piyushawasthi)) -* Updated code to work with latest azure-sdk gems. [\#425](https://github.com/chef/knife-azure/pull/425)([dheerajd-msys](https://github.com/dheerajd-msys)) \ No newline at end of file diff --git a/Rakefile b/Rakefile index 514c7001..452326e9 100755 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,5 @@ # -# Copyright:: Copyright 2010-2019, Chef Software Inc. +# Copyright:: Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -57,18 +57,4 @@ rescue LoadError puts "chefstyle gem is not installed. bundle install first to make sure all dependencies are installed." end -begin - require "yard" unless defined?(YARD) - YARD::Rake::YardocTask.new(:docs) -rescue LoadError - puts "yard is not available. bundle install first to make sure all dependencies are installed." -end - -task :console do - require "irb" - require "irb/completion" - ARGV.clear - IRB.start -end - task default: %i{style spec} diff --git a/docs/ASM.md b/docs/ASM.md deleted file mode 100644 index 9939c83b..00000000 --- a/docs/ASM.md +++ /dev/null @@ -1,337 +0,0 @@ -## Basic Examples for ASM - -The following examples assume that you've configured the publishsettings file -location in your config.rb: - - # List images for use in creating new VM's: - $ knife azure image list - - # List all VM's (including those not be managed by Chef) - $ knife azure server list - - # Create and bootstrap a Windows VM over winrm (winrm is the default for Windows) - $ knife azure server create --azure-dns-name MyNewServerName --azure-vm-size Standard_A2 -I a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-20150825-en.us-127GB.vhd --azure-service-location 'West US' --connection-user myuser --connection-password 'mypassword' - - # Create and bootstrap a Windows VM over winrm using SSL (winrm is the default for Windows) - $ knife azure server create --azure-dns-name MyNewServerName --azure-vm-size Standard_A2 -I a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-20150825-en.us-127GB.vhd --azure-service-location 'West US' --connection-user myuser --connection-password 'mypassword' --winrm-ssl --winrm-no-verify-cert - - # Create and bootstrap an Ubuntu VM over ssh - $ knife azure server create -N MyNewNode --azure-vm-size Standard_A2 -I b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20140927-en-us-30GB -m 'West US' --connection-user myuser --ssh-identity-file ~/.ssh/myprivatekey_rsa - - # Create and bootstrap an Windows VM through the Azure API -- - # No winrm or ssh transport or Internet access required - $ knife azure server create --azure-dns-name MyNewServerName --azure-vm-size Standard_A2 -I a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-20150825-en.us-127GB.vhd --azure-service-location 'West US' --connection-user myuser --connection-password 'mypassword' --connection-protocol cloud-api - - # Delete a server and purge it from the Chef server - $ knife azure server delete MyNewNode --purge -y - -Use the --help option to read more about each subcommand. Eg: - - knife azure server create --help - - -## Detailed Usage for ASM mode - -### Common Configuration -Most configuration options can be specified either in your config.rb file or as command line parameters. The CLI parameters override the config.rb parameters. - -The following options are required for all subcommands: - - option :azure_publish_settings_file Path to your .publishsettings file - -OR - - option :azure_subscription_id Your Azure subscription ID - option :azure_mgmt_cert Management certificate in PEM format - option :azure_api_host_name Your Azure API host name - -### Azure Image List Subcommand -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. - - knife azure image list - -### Azure Server Create Subcommand -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. - -#### Windows Bootstrapping Requirements -Windows source images should have the WinRM service enabled and the -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: -https://github.com/chef/knife-windows#nodes - -#### Azure-specific Options - --azure-dns-name Required. The DNS prefix name that can be used to access the cloud - service which is unique within Microsoft Azure. If you want to add - new VM to an existing service/deployment, specify an exiting - dns-name, along with --azure-connect-to-existing-dns option. Otherwise - a new deployment is created. - --azure-service-location Required. Specifies the geographic location of the resource as the - name of a datacenter location that is valid for your subscription. Eg: - West US, East US, East Asia, Southeast Asia, North Europe, West Europe. - --azure-source-image Required. Specifies the name of the disk image to use to create - the virtual machine. Do a "knife azure image list" to see a - list of available images. - --azure-storage-account A name for the storage account that is unique within Microsoft Azure. - Storage account names must be between 3 and 24 characters in - length and use numbers and lower-case letters only. This name is - the DNS prefix name and can be used to access blobs, queues, and - tables in the storage account. - --azure-vm-name Specifies the name for the virtual machine. The name must - be unique within the deployment. - --azure-os-disk-name Optional. Specifies the friendly name of the disk containing - the guest OS image in the image repository. - --azure-vm-size Size of virtual machine. Default is Standard_A1_v2. - Eg: Standard_A2, Standard_F2, Standard_G1, Standard_F2 etc. - --azure-connect-to-existing-dns Set this flag to add the new VM to an existing - deployment/service. Must give the name of the existing - DNS correctly in the --azure-dns-name option - --azure-availability-set Optional. Name of availability set to add virtual machine into. - -Note: For all valid --azure-vm-size values you can refer to this [docs](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sizes). - -#### Azure VM Quick Create -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): - - knife azure server create - --azure-publish-settings-file '/path/to/your/cert.publishsettingsfile' - --azure-dns-name 'myservice' - --azure-service-location 'West US' - --azure-source-image 'source-image-name' - --connection-user 'jetstream' - --ssh-identity-file '~/.ssh/myazure_rsa' - -Note that the --ssh-identity-file option, which enables specification of a private -key authorized to communicate securely with the created server during the -bootstrap process, will also configure the user specified by --connection-user with -the public key that corresponds to the private key specified by ---ssh-identity-file. This configuration persists even after the create subcommand -has completed successfully, so that the key specified with --ssh-identity-file can -be used with ssh clients for subsequent access to the server as the user -specified by --connection-user. - -You can set these options from config.rb. A typical config.rb is -shown below: - - knife[:azure_publish_settings_file] = '/path/to/your/cert.publishsettingsfile' - knife[:azure_dns_name] = 'myservice' - knife[:azure_service_location] = 'West US' - knife[:azure_source_image] = 'source-image-name' - knife[:connection_user] = 'jetstream' - knife[:ssh_identity_file] = '~/.ssh/myazure_rsa' - -#### Azure VM Advanced Create -You can set various other options in the advanced create. - Eg: If you want to set the Azure VM Name different from that of the Azure DNS Name, set the option :azure_vm_name. - Eg: If you want to specify a Storage Account Name, set the option :azure_storage_account - -To connect to an existing DNS/service, you can use a command as below: - - knife azure server create - --azure-subscription-id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - --azure-mgmt-cert '/path/to/your/mgmtCert.pem' - --azure-api-host-name 'management.core.windows.net' - --azure-connect-to-existing-dns - --azure-dns-name 'myservice' - --azure-vm-name 'myvm02' - --azure-service-location 'West US' - --azure-source-image 'source-image-name' - --connection-user 'jetstream' - --connection-password 'jetstream@123' - -These options may also be configured from config.rb, as in this example: - - knife[:azure_subscription_id] = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - knife[:azure_mgmt_cert] = '/path/to/your/mgmtCert.pem' - knife[:azure_api_host_name] = 'management.core.windows.net' - knife[:azure_service_location] = 'West US' - knife[:azure_dns_name]='myservice' - knife[:azure_vm_name]='myvm02' - knife[:connection_user]='jetstream' - knife[:ssh_identity_file]='/path/to/RSA/private/key' - knife[:azure_storage_account]='auxpreview104' - knife[:azure_os_disk_name]='disk107' - knife[:tcp-endpoints]='80:80,3389:5678' - knife[:udp-endpoints]='123:123' - -#### Endpoint configuration - -Endpoints are configured using tcp-endpoints and udp-endpoints. This is a string in the form: -{localPort}:{publicPort}:{load_balancer_set_name}:{load_balancer_probe_path} - -Examples: - - knife[:tcp-endpoints]='80' # Allow Port 80 inbound - knife[:tcp-endpoints]='80:8080' # Allow Port 80 inbound and map it to local port 8080 - knife[:tcp-endpoints]='80:8080:web-set' # Allow Port 80 and add it to the load balancing set called 'web-set' - knife[:tcp-endpoints]='80:8080:web-set:/healthcheck' # Allow Port 80, add it to the load balancing set, and use an HTTP probe at path "/healthcheck" - -Note that the load balancing set will be created if it does not exist. If it exists within another VM in the cloud service, it will re-use those values for the probe. - -#### Options for Bootstrapping a Windows Node in Azure - - :connection_protocol The protocol to use to connect to the target node. (valid options: 'ssh' or 'winrm') - :connection_password Authenticate to the target host with this password - :winrm_auth_method The WinRM authentication method to use. (valid options: 'plaintext', 'kerberos', 'ssl', or 'negotiate') - :winrm_ssl Use SSL in the WinRM connection - :connection_port The port on the target node to connect to - :ca_trust_file The CA certificate file to use to verify the server when using SSL - :winrm_no_verify_cert Do not verify the SSL certificate of the target node for WinRM - :kerberos_realm The Kerberos realm used for authentication - :kerberos_service The Kerberos service used for authentication - - -#### Options to configure WinRM for Bootstrapping a Windows Node -Theses options are useful if you have long-running run-lists and if the chef run might use a lot of memory. In most cases people don't need to set these, but if they see certain timeout or memory related errors during bootstrap, particularly on Win2k8r2, it may make sense to move these beyond the default. - - :winrm_max_timeout Set winrm max timeout in minutes - :winrm_max_memory_per_shell Set winrm max memory per shell in MB - - Command: - knife azure server create - --azure-dns-name 'myserver' - --azure-source-image 'windows-2012-image-id' - --azure-service-location 'West US' - --connection-user azure - --connection-password 'azure@123' - --winrm-max-timeout 30 - --winrm-max-memory-per-shell 400 - -#### Azure Windows Node Create -The quick create option requires the following options for a windows instance: - - knife azure server create - --azure-publish-settings-file '/path/to/your/cert.publishsettingsfile' - --azure-dns-name 'myserverdnsname' - --azure-service-location 'West US' - --azure-source-image 'windows-2012-image-id' - --connection-user 'jetstream' - --connection-password 'jetstream@123' - -Sample config.rb for bootstrapping Windows Node with basic authentication - - knife[:connection_protocol] = 'winrm' - knife[:connection_password] = 'mgcvTuvV2Rh' - knife[:connection_user] = 'myuser' - knife[:connection_port] = '5985' - knife[:azure_source_image]='windows-2012-image-id' - -#### `cloud-api` bootstrap feature -By specifying the value `cloud-api` for the `connection_protocol` option of `knife azure server create` instead of `winrm` or `ssh`, Microsoft Azure will install Chef Client using the `azure-chef-extension`. The process as a whole is asynchronous, so once the `knife azure server create` command has create the VM, full provisioning and Chef bootstrap will continue to occur even if the `knife` command is terminated before it completes. - -We have added option `daemon` for Windows OS which configures the chef-client as a service or as a scheduled task for unattended execution. Accepted values are `none`, `service` and `task`. - none - Currently prevents the chef-client service or scheduled task to be configured. - service - Configures the chef-client to run automatically in the background as a service. - task - Configures the chef-client to run automatically in the background as a scheduled task. So chef-client runs in a defined interval which is 30 mins by default. - -Option `chef_daemon_interval` can be used for running the chef-client as a service or as a scheduled task in defined interval automatically in the background. Its value is 30 mins by default. - - - knife azure server create - --azure-publish-settings-file '/path/to/your/cert.publishsettingsfile' - --azure-dns-name 'myserverdnsname' - --azure-service-location 'West US' - --azure-source-image 'windows-2012-image-id' - --connection-user 'jetstream' - --connection-password 'jetstream@123' - --connection-protocol 'cloud-api' - --daemon 'task' - --chef-daemon-interval '18' - - -It's possible to pass bootstrap options to the extension which get specified in `client.rb` file on the VM. Following options can be passed: - - --environment - --node-name - --secret-file - --server-url - --validation-client-name - --[no-]node-verify-api-cert - --bootstrap-version - --node-ssl-verify-mode - --bootstrap-proxy - --chef-daemon-interval - --extended-logs - - -#### Azure Server Create with Domain Join -Following options are used for creating server with domain join - - :azure_domain_name Specifies the domain name to join. If the domains name is not specified, --azure-domain-user must specify the user principal name (UPN) format (user@fully-qualified-DNS-domain) or the fully-qualified-DNS-domain\\username format - :azure_domain_user Specifies the username who has access to join the domain.Supported format: username(if domain is already specified in --azure-domain-name option),fully-qualified-DNS-domain\username, user@fully-qualified-DNS-domain - :azure_domain_passwd Specifies the password for domain user who has access to join the domain - - Command: - knife azure server create -I a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-Datacenter-20151022-en.us-127GB.vhd --azure-vm-size Standard_A2 -U 'azure' -P 'admin@123' --azure-domain-passwd 'admin@123' --azure-domain-user 'some.domain.com\user' --azure-domain-name 'some.domain.com' -c '~\chef-repo\.chef\config.rb' --azure-network-name 'mynetwork' --azure-subnet-name 'subnet1' --azure-service-location 'West US' - - -### Azure Server Delete Subcommand -Deletes an existing server in the currently configured Azure account. By -default, this does not delete the associated node and client objects from the -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. The underlying VHD blob is also deleted by default. 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. - - knife azure server delete "myvm01" - knife azure server delete "myvm01" --purge #purge chef node - knife azure server delete "myvm01" --preserve-azure-os-disk - knife azure server delete "myvm01" --preserve-azure-vhd - knife azure server delete "myvm01" --preserve-azure-dns-name - knife azure server delete "myvm01" --delete-azure-storage-account - -Since the VM name can be the same across DNS name, you must specify the DNS -name also to delete the VM. Sample command to delete a VM for a given DNS name: - - knife azure server delete "myvm01" --azure-dns-name "mydnsname" - knife azure server delete "myvm01" "myvm02" --azure-dns-name "mydnsname" - -### Azure Server List Subcommand -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. - - knife azure server list - -### Azure AG List Subcommand -Outputs a list of defined affinity groups in the azure subscription. - - knife azure ag list - -### Azure AG Create Subcommand -Creates a new affinity group in the specified service location. - - knife azure ag create -a 'mynewag' -m 'West US' --azure-ag-desc 'Optional Description' - -Knife options: - - :azure_affinity_group Specifies new affinity group name. - :azure_service_location Specifies the geographic location. - :azure_ag_desc Optional. Description for new affinity group. - -### Azure Internal LB List Subcommand -Outputs a lit of defined load balancers for all cloud services. Public facing load balancers are not shown here. - -### Azure Internal LB Create Subcommand -Creates a new Internal Load Balancer within a cloud service. - - knife azure internal lb create -n 'my_lb' --azure-lb-static-vip '10.0.0.123' --azure-subnet_name 'Subnet_1' --azure-dns-name 'service_name' - -Knife options: - :azure_load_balancer Required. Specifies the name of the Load Balancer. - :azure_lb_static_vip Optional. Allows you to set a static IP for the VIP. - :azure_subnet_name Required ONLY IF azure_lb_static_ip is set. Specifies the subnet that the static IP resides in. - :azure_dns_name Required. The cloud service that this internal Load Balancer will be added to. - -### Azure Vnet List Subcommand -Outputs a list of defined virtual networks in the azure subscription. - - knife azure vnet list - -### Azure Vnet Create Subcommand -Creates a new or modifies an existing virtual network. If an existing virtual network is named, the -affinity group and address space are replaced with the new values. - - knife azure vnet create -n 'mynewvn' -a 'existingag' --azure_address_space '10.0.0.0/24' - -Knife options: - - :azure_network_name Specifies the name of the virtual network to create. - :azure_affinity_group Specifies the affinity group to associate with the vnet. - :azure_address_space Specifies the address space of the vnet using CIDR notation. - -For CIDR notation, see here: http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing -Address available are defined in RFC 1918: http://en.wikipedia.org/wiki/Private_network diff --git a/docs/bootstrap.md b/docs/bootstrap.md index d8817da1..34ca24d1 100644 --- a/docs/bootstrap.md +++ b/docs/bootstrap.md @@ -11,15 +11,3 @@ Bootstrap existing Azure ARM VM using following command: You can use other options like --azure-dns-name, --bootstrap-version with this command and there are many more options which can be used with this command. Use --help to identify more options. NOTE: This command only works for single VM bootstrap. - -## ASM mode - -Bootstrap existing Azure ASM VM using following command: - -```knife bootstrap azure ``` - -```$ knife bootstrap azure myVMName``` - -You can use other options like --azure-dns-name, --bootstrap-version with this command. There are many more options which can be used with this command. Use --help to identify more options. - -NOTE: This command only works for single VM bootstrap. diff --git a/docs/configuration.md b/docs/configuration.md index cea242e9..56a5c6bf 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -23,34 +23,6 @@ knife[:azure_client_secret] # password you set at initially *Microsoft Azure encourages the use of Azure CLI 2.0. If you are still using [azure-xplat-cli](https://github.com/Azure/azure-xplat-cli) then you may simply run ```azure login``` and skip creating the service principal entirely.* -## ASM mode - -For this plugin to interact with Azure's REST API, you will need to give Knife -information about your Azure account and credentials. The easiest way to do -this is to sign in to the Azure portal and download a publishsettings file -from https://manage.windowsazure.com/publishsettings/index?client=xplat to a -local file system location, and -then refer to the local file via an entry in your knife.rb: - - knife[:azure_publish_settings_file] = "~/myazure.publishsettings" - -Alternatively, all subcommands for this plugin will accept an ---azure-publish-settings-file option to allow you to specify the path to that -file with each command invocation. - -The plug-in also accepts authentication information specified using an -alternative set of options -- see the section on [Alternative Management -Certificate Specification] (#alternative-management-certificate-specification) for details. - -The plug-in can also read Azure account and credentials from the `Azure Profile` if Knife does not have the entry for `publish_settings_file`. -An `Azure Profile` is a `JSON` file with subscription and environment information in it. Its default location is `~/.azure/azureProfile.json`. - -The Azure Profile file can be created and manipulated using the [Azure CLI](http://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/). You can -also refer [Azure Xplat-CLI](https://github.com/Azure/azure-xplat-cli#use-publish-settings-file-management-certificate-authentication). - -If Azure Profile file has entries for multiple subscriptions then you can choose the default using `azure account set `. The same default subscription will -be picked up that you have configured. - ## Alternative Management Certificate Specification In addition to specifying the management certificate using the publishsettings diff --git a/knife-azure.gemspec b/knife-azure.gemspec index 708b44b2..ada79e65 100755 --- a/knife-azure.gemspec +++ b/knife-azure.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Barry Davis", "Chirag Jog"] - s.summary = "A plugin to the Chef knife tool for creating instances on the Microsoft Azure platform" + s.summary = "A plugin to the Chef Infra knife tool for creating instances on the Microsoft Azure platform" s.description = s.summary s.email = "oss@chef.io" s.licenses = ["Apache 2.0"] @@ -17,15 +17,13 @@ Gem::Specification.new do |s| s.files = %w{LICENSE} + Dir.glob("lib/**/*") s.homepage = "https://github.com/chef/knife-azure" s.require_paths = ["lib"] - s.required_ruby_version = ">= 2.6" + s.required_ruby_version = ">= 2.7" - s.add_dependency "chef", ">= 15.10.21" # needs this version for Chef 16 backports - s.add_dependency "nokogiri", ">= 1.5.5" + s.add_dependency "knife" s.add_dependency "azure_mgmt_resources", "~> 0.17", ">= 0.17.2" s.add_dependency "azure_mgmt_compute", "~> 0.18", ">= 0.18.3" s.add_dependency "azure_mgmt_storage", "~> 0.20", ">= 0.20.0" s.add_dependency "azure_mgmt_network", "~> 0.18", ">= 0.18.2" s.add_dependency "listen", "~> 3.1" s.add_dependency "ipaddress" - s.add_dependency "ffi" end diff --git a/lib/azure/custom_errors.rb b/lib/azure/custom_errors.rb index 724710d0..804ac0de 100644 --- a/lib/azure/custom_errors.rb +++ b/lib/azure/custom_errors.rb @@ -29,6 +29,6 @@ def api_not_implemented(klass) caller.first =~ /in \`(.+)\'/ method_name = $1 raise CustomErrors::InterfaceNotImplementedError.new("#{klass.class.name} needs to implement '#{method_name}' for interface #{name}!") - end + end end end diff --git a/lib/azure/resource_management/ARM_deployment_template.rb b/lib/azure/resource_management/ARM_deployment_template.rb index d4b2a40c..8a15ccfb 100644 --- a/lib/azure/resource_management/ARM_deployment_template.rb +++ b/lib/azure/resource_management/ARM_deployment_template.rb @@ -51,7 +51,7 @@ def tcp_ports(tcp_ports, vm_name) # Security Rule priority can be set between 100 and 4096 rule_no = 300 incremental = 0 - for port in tcp_ports + tcp_ports.each do |port| rule_no += 2 sec_grp_json["properties"]["securityRules"].push( "name" => vm_name + "_rule_" + incremental.to_s, @@ -510,7 +510,7 @@ def create_deployment_template(params) } length = template["resources"].length.to_i - 1 - for i in 0..length do + (0..length).each do |i| if template["resources"][i]["type"] == "Microsoft.Compute/virtualMachines" template["resources"][i]["dependsOn"] << "[concat('Microsoft.Compute/availabilitySets/', parameters('availabilitySetName'))]" template["resources"][i]["properties"]["availabilitySet"] = { "id" => "[resourceId('Microsoft.Compute/availabilitySets', parameters('availabilitySetName'))]" } @@ -523,7 +523,7 @@ def create_deployment_template(params) sec_grp_json = tcp_ports(params[:tcp_endpoints], params[:azure_vm_name]) template["resources"].insert(1, sec_grp_json) length = template["resources"].length.to_i - 1 - for i in 0..length do + (0..length).each do |i| if template["resources"][i]["type"] == "Microsoft.Network/virtualNetworks" template["resources"][i] = template["resources"][i].merge({ "dependsOn" => [sec_grp] }) end @@ -558,7 +558,7 @@ def create_deployment_template(params) end if params[:server_count].to_i > 1 && params[:chef_extension_private_param][:validation_key].nil? template["resources"].last["properties"]["protectedSettings"]["client_pem"] = "[parameters(concat('client_pem',copyIndex()))]" - 0.upto (params[:server_count].to_i - 1) do |count| + 0.upto(params[:server_count].to_i - 1) do |count| template["parameters"]["client_pem" + count.to_s] = { "type" => "string", "metadata" => { @@ -658,7 +658,7 @@ def create_deployment_parameters(params) }, } if params[:server_count].to_i > 1 && params[:chef_extension_private_param][:validation_key].nil? - 0.upto (params[:server_count].to_i - 1) do |count| + 0.upto(params[:server_count].to_i - 1) do |count| parameters["client_pem#{count}"] = { "value" => "#{params[:chef_extension_private_param][("client_pem" + count.to_s).to_sym]}", } diff --git a/lib/azure/resource_management/ARM_interface.rb b/lib/azure/resource_management/ARM_interface.rb index 62cd54a0..2bb34f90 100644 --- a/lib/azure/resource_management/ARM_interface.rb +++ b/lib/azure/resource_management/ARM_interface.rb @@ -307,7 +307,7 @@ def fetch_chef_client_logs(resource_group_name, virtual_machine_name, chef_exten ## chef-client run did not complete within maximum timeout of 30 minutes ## ## fetch whatever logs available under the chef-client.log file ## color = :yellow - end + end puts ui.color(status, color, :bold).to_s puts "----> chef-client run logs: " puts "\n#{message}\n" ## message field of substatus contains the chef-client run logs ## @@ -484,11 +484,9 @@ def common_arm_rescue_block(error) err_details = err_json["error"]["details"] if err_json["error"] if err_details err_details.each do |err| - begin - ui.error(JSON.parse(err["message"])["error"]["message"]) - rescue JSON::ParserError => e - ui.error(err["message"]) - end + ui.error(JSON.parse(err["message"])["error"]["message"]) + rescue JSON::ParserError => e + ui.error(err["message"]) end else ui.error(err_json["error"]["message"]) diff --git a/lib/azure/resource_management/windows_credentials.rb b/lib/azure/resource_management/windows_credentials.rb index b3ca504c..2a1ac493 100644 --- a/lib/azure/resource_management/windows_credentials.rb +++ b/lib/azure/resource_management/windows_credentials.rb @@ -66,7 +66,7 @@ class CREDENTIAL_OBJECT < FFI::Struct :Attributes, CREDENTIAL_ATTRIBUTE, :TargetAlias, :LPTSTR, :UserName, :LPTSTR - end + end # Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374804(v=vs.85).aspx safe_attach_function :CredReadW, %i{LPCTSTR DWORD DWORD pointer}, :BOOL @@ -178,5 +178,5 @@ def latest_credential_target(targets) end end end -end + end end diff --git a/lib/azure/service_management/ASM_interface.rb b/lib/azure/service_management/ASM_interface.rb deleted file mode 100644 index ad059b72..00000000 --- a/lib/azure/service_management/ASM_interface.rb +++ /dev/null @@ -1,310 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../azure_interface" -require_relative "rest" -require_relative "connection" - -module Azure - class ServiceManagement - class ASMInterface < AzureInterface - include AzureAPI - - attr_accessor :connection - - def initialize(params = {}) - @rest = Rest.new(params) - @connection = Azure::ServiceManagement::Connection.new(@rest) - super - end - - def list_images - connection.images.all - end - - def list_servers - servers = connection.roles.all - cols = ["DNS Name", "VM Name", "Status", "IP Address", "SSH Port", "WinRM Port", "RDP Port"] - rows = [] - servers.each do |server| - rows << server.hostedservicename.to_s + ".cloudapp.net" # Info about the DNS name at http://msdn.microsoft.com/en-us/library/ee460806.aspx - rows << server.name.to_s - rows << begin - state = server.status.to_s.downcase - case state - when "shutting-down", "terminated", "stopping", "stopped" - ui.color(state, :red) - when "pending" - ui.color(state, :yellow) - else - ui.color("ready", :green) - end - end - rows << server.publicipaddress.to_s - rows << server.sshport.to_s - rows << server.winrmport.to_s - ports = server.tcpports - rows << rdp_port(ports) - end - display_list(ui, cols, rows) - end - - def rdp_port(arr_ports) - unless arr_ports - return "" - end - - if arr_ports.length > 0 - arr_ports.each do |port| - if port["Name"] == "Remote Desktop" - return port["PublicPort"] - end - end - end - "" - end - - def find_server(params = {}) - server = connection.roles.find(params[:name], params = { azure_dns_name: params[:azure_dns_name] }) - end - - def delete_server(params = {}) - server = find_server({ name: params[:name], azure_dns_name: params[:azure_dns_name] }) - - unless server - puts "\n" - ui.error("Server #{params[:name]} does not exist") - exit! - end - - puts "\n" - msg_pair(ui, "DNS Name", server.hostedservicename + ".cloudapp.net") - msg_pair(ui, "VM Name", server.name) - msg_pair(ui, "Size", server.size) - msg_pair(ui, "Public Ip Address", server.publicipaddress) - puts "\n" - - begin - ui.confirm("Do you really want to delete this server") - rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception - server = nil # Cleanup is implicitly performed in other cloud plugins - exit! - end - - params[:azure_dns_name] = server.hostedservicename - - connection.roles.delete(params) - - puts "\n" - ui.warn("Deleted server #{server.name}") - end - - def show_server(name) - role = connection.roles.find name - - puts "" - if role - details = [] - details << ui.color("Role name", :bold, :cyan) - details << role.name - details << ui.color("Status", :bold, :cyan) - details << role.status - details << ui.color("Size", :bold, :cyan) - details << role.size - details << ui.color("Hosted service name", :bold, :cyan) - details << role.hostedservicename - details << ui.color("Deployment name", :bold, :cyan) - details << role.deployname - details << ui.color("Host name", :bold, :cyan) - details << role.hostname - unless role.sshport.nil? - details << ui.color("SSH port", :bold, :cyan) - details << role.sshport - end - unless role.winrmport.nil? - details << ui.color("WinRM port", :bold, :cyan) - details << role.winrmport - end - details << ui.color("Public IP", :bold, :cyan) - details << role.publicipaddress.to_s - - unless role.thumbprint.empty? - details << ui.color("Thumbprint", :bold, :cyan) - details << role.thumbprint - end - puts ui.list(details, :columns_across, 2) - if role.tcpports.length > 0 || role.udpports.length > 0 - details.clear - details << ui.color("Ports open", :bold, :cyan) - details << ui.color("Local port", :bold, :cyan) - details << ui.color("IP", :bold, :cyan) - details << ui.color("Public port", :bold, :cyan) - if role.tcpports.length > 0 - role.tcpports.each do |port| - details << "tcp" - details << port["LocalPort"] - details << port["Vip"] - details << port["PublicPort"] - end - end - if role.udpports.length > 0 - role.udpports.each do |port| - details << "udp" - details << port["LocalPort"] - details << port["Vip"] - details << port["PublicPort"] - end - end - puts ui.list(details, :columns_across, 4) - end - else - puts "No VM found" - end - - rescue => error - puts "#{error.class} and #{error.message}" - end - - def list_internal_lb - lbs = connection.lbs.all - cols = %w{Name Service Subnet VIP} - rows = [] - lbs.each do |lb| - cols.each { |col| rows << lb.send(col.downcase).to_s } - end - display_list(ui, cols, rows) - end - - def create_internal_lb(params = {}) - connection.lbs.create(params) - end - - def list_vnets - vnets = connection.vnets.all - cols = ["Name", "Affinity Group", "State"] - rows = [] - vnets.each do |vnet| - %w{name affinity_group state}.each { |col| rows << vnet.send(col).to_s } - end - display_list(ui, cols, rows) - end - - def create_vnet(params = {}) - connection.vnets.create(params) - end - - def list_affinity_groups - affinity_groups = connection.ags.all - cols = %w{Name Location Description} - rows = [] - affinity_groups.each do |affinity_group| - cols.each { |col| rows << affinity_group.send(col.downcase).to_s } - end - display_list(ui, cols, rows) - end - - def create_affinity_group(params = {}) - connection.ags.create(params) - end - - def create_server(params = {}) - remove_hosted_service_on_failure = params[:azure_dns_name] - if connection.hosts.exists?(params[:azure_dns_name]) - remove_hosted_service_on_failure = nil - end - - # If Storage Account is not specified, check if the geographic location has one to re-use - if not params[:azure_storage_account] - storage_accts = connection.storageaccounts.all - storage = storage_accts.find { |storage_acct| storage_acct.location.to_s == params[:azure_service_location] } - unless storage - params[:azure_storage_account] = [strip_non_ascii(params[:azure_vm_name]), random_string].join.downcase - remove_storage_service_on_failure = params[:azure_storage_account] - else - remove_storage_service_on_failure = nil - params[:azure_storage_account] = storage.name.to_s - end - else - if connection.storageaccounts.exists?(params[:azure_storage_account]) - remove_storage_service_on_failure = nil - else - remove_storage_service_on_failure = params[:azure_storage_account] - end - end - - begin - connection.deploys.create(params) - rescue Exception => e - Chef::Log.error("Failed to create the server -- exception being rescued: #{e}") - backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" - Chef::Log.debug("#{backtrace_message}") - cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure) - end - end - - def cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure) - Chef::Log.warn("Cleaning up resources...") - - if remove_hosted_service_on_failure - ret_val = connection.hosts.delete(remove_hosted_service_on_failure) - ret_val.content.empty? ? Chef::Log.warn("Deleted created DNS: #{remove_hosted_service_on_failure}.") : Chef::Log.warn("Deletion failed for created DNS:#{remove_hosted_service_on_failure}. " + ret_val.text) - end - - if remove_storage_service_on_failure - ret_val = connection.storageaccounts.delete(remove_storage_service_on_failure) - ret_val.content.empty? ? Chef::Log.warn("Deleted created Storage Account: #{remove_storage_service_on_failure}.") : Chef::Log.warn("Deletion failed for created Storage Account: #{remove_storage_service_on_failure}. " + ret_val.text) - end - exit 1 - end - - def get_role_server(dns_name, vm_name) - deploy = connection.deploys.queryDeploy(dns_name) - deploy.find_role(vm_name) - end - - def get_extension(name, publisher) - connection.query_azure("resourceextensions/#{publisher}/#{name}") - end - - def deployment_name(dns_name) - connection.deploys.get_deploy_name_for_hostedservice(dns_name) - end - - def deployment(path) - connection.query_azure(path) - end - - def valid_image?(name) - connection.images.exists?(name) - end - - def vm_image?(name) - connection.images.is_vm_image(name) - end - - def add_extension(name, params = {}) - ui.info "Started with Chef Extension deployment on the server #{name}..." - connection.roles.update(name, params) - ui.info "\nSuccessfully deployed Chef Extension on the server #{name}." - rescue Exception => e - Chef::Log.error("Failed to add extension to the server -- exception being rescued: #{e}") - backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" - Chef::Log.debug("#{backtrace_message}") - end - end - end -end diff --git a/lib/azure/service_management/ag.rb b/lib/azure/service_management/ag.rb deleted file mode 100644 index 3f7a0c51..00000000 --- a/lib/azure/service_management/ag.rb +++ /dev/null @@ -1,99 +0,0 @@ -# -# Author:: Jeff Mendoza (jeffmendoza@live.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class AGs - def initialize(connection) - @connection = connection - end - - def load - @ags ||= begin - @ags = {} - response = @connection.query_azure("affinitygroups", - "get", - "", - "", - true, - false) - response.css("AffinityGroup").each do |ag| - item = AG.new(@connection).parse(ag) - @ags[item.name] = item - end - @ags - end - end - - def all - load.values - end - - def exists?(name) - load.key?(name) - end - - def find(name) - load[name] - end - - def create(params) - ag = AG.new(@connection) - ag.create(params) - end - end -end - -module Azure - class AG - attr_accessor :name, :label, :description, :location - - def initialize(connection) - @connection = connection - end - - def parse(image) - @name = image.at_css("Name").content - @label = image.at_css("Label").content - @description = image.at_css("Description").content if - image.at_css("Description") - @location = image.at_css("Location").content if image.at_css("Location") - self - end - - def create(params) - builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml| - xml.CreateAffinityGroup( - xmlns: "http://schemas.microsoft.com/windowsazure" - ) do - xml.Name params[:azure_ag_name] - xml.Label Base64.strict_encode64(params[:azure_ag_name]) - unless params[:azure_ag_desc].nil? - xml.Description params[:azure_ag_desc] - end - xml.Location params[:azure_location] - end - end - @connection.query_azure("affinitygroups", - "post", - builder.to_xml, - "", - true, - false) - end - end -end diff --git a/lib/azure/service_management/certificate.rb b/lib/azure/service_management/certificate.rb deleted file mode 100755 index 24b599bb..00000000 --- a/lib/azure/service_management/certificate.rb +++ /dev/null @@ -1,235 +0,0 @@ -# -# Author:: Mukta Aphale (mukta.aphale@clogeny.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class Certificates - def initialize(connection) - @connection = connection - end - - def create(params) - certificate = Certificate.new(@connection) - certificate.create(params) - end - - def add(certificate_data, certificate_password, certificate_format, dns_name) - certificate = Certificate.new(@connection) - certificate.add_certificate certificate_data, certificate_password, certificate_format, dns_name - end - - def create_ssl_certificate(azure_dns_name) - cert_params = { output_file: "winrm", key_length: 2048, cert_validity: 24, - azure_dns_name: azure_dns_name } - certificate = Certificate.new(@connection) - thumbprint = certificate.create_ssl_certificate(cert_params) - end - - def get_certificate(dns_name, fingerprint) - certificate = Certificate.new(@connection) - certificate.get_certificate(dns_name, fingerprint) - end - end -end - -module 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[:ssh_identity_file], - ssh_key_passphrase: params[:identity_file_passphrase] }) - add_certificate @cert_data, "knifeazure", "pfx", params[:azure_dns_name] - - # 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.new("SHA256")) - # 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 - - def add_certificate(certificate_data, certificate_password, certificate_format, dns_name) - # 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") do - xml.Data certificate_data - xml.CertificateFormat certificate_format - xml.Password certificate_password - end - end - # Windows Azure API call - @connection.query_azure("hostedservices/#{dns_name}/certificates", "post", builder.to_xml) - - # Check if certificate is available else raise error - for attempt in 0..4 - Chef::Log.info "Waiting to get certificate ..." - res = get_certificate(dns_name, @fingerprint) - break unless res.empty? - if attempt == 4 - raise "The certificate with thumbprint #{fingerprint} was not found." - else - sleep 5 - end - end - end - - def get_certificate(dns_name, fingerprint) - @connection.query_azure("hostedservices/#{dns_name}/certificates/sha1-#{fingerprint}", "get").search("Certificate") - end - - ######## SSL certificate generation for knife-azure ssl bootstrap ###### - def create_ssl_certificate(cert_params) - file_path = cert_params[:output_file].sub(/\.(\w+)$/, "") - path = prompt_for_file_path - file_path = File.join(path, file_path) unless path.empty? - cert_params[:domain] = prompt_for_domain - - rsa_key = generate_keypair cert_params[:key_length] - cert = generate_certificate(rsa_key, cert_params) - write_certificate_to_file cert, file_path, rsa_key, cert_params - puts "*" * 70 - puts "Generated Certificates:" - puts "- #{file_path}.pfx - PKCS12 format keypair. Contains both the public and private keys, usually used on the server." - puts "- #{file_path}.b64 - Base64 encoded PKCS12 keypair. Contains both the public and private keys, for upload to the Azure REST API." - puts "- #{file_path}.pem - Base64 encoded public certificate only. Required by the client to connect to the server." - puts "Certificate Thumbprint: #{@thumbprint.to_s.upcase}" - puts "*" * 70 - - config[:ca_trust_file] = file_path + ".pem" if config[:ca_trust_file].nil? - cert_data = File.read (file_path + ".b64") - add_certificate cert_data, @winrm_cert_passphrase, "pfx", cert_params[:azure_dns_name] - @thumbprint - end - - def generate_keypair(key_length) - OpenSSL::PKey::RSA.new(key_length.to_i) - end - - def prompt_for_passphrase - passphrase = "" - begin - print "Passphrases do not match. Try again.\n" unless passphrase.empty? - print "Enter certificate passphrase (empty for no passphrase):" - passphrase = STDIN.gets - return passphrase.strip if passphrase == "\n" - - print "Enter same passphrase again:" - confirm_passphrase = STDIN.gets - end until passphrase == confirm_passphrase - passphrase.strip - end - - def prompt_for_file_path - file_path = "" - counter = 0 - begin - print "Invalid location! \n" unless file_path.empty? - print 'Enter the file path for certificates e.g. C:\Windows (empty for current location):' - file_path = STDIN.gets - stripped_file_path = file_path.strip - return stripped_file_path if file_path == "\n" - - counter += 1 - exit(1) if counter == 3 - end until File.directory?(stripped_file_path) - stripped_file_path - end - - def prompt_for_domain - counter = 0 - begin - print "Enter the domain (mandatory):" - domain = STDIN.gets - domain = domain.strip - counter += 1 - exit(1) if counter == 3 - end until !domain.empty? - domain - end - - def generate_certificate(rsa_key, cert_params) - @hostname = "*" - if cert_params[:domain] - @hostname = "*." + cert_params[:domain] - end - - # Create a self-signed X509 certificate from the rsa_key (unencrypted) - cert = OpenSSL::X509::Certificate.new - cert.version = 2 - cert.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect - - cert.subject = OpenSSL::X509::Name.parse "/CN=#{@hostname}" - cert.issuer = cert.subject - cert.public_key = rsa_key.public_key - cert.not_before = Time.now - cert.not_after = cert.not_before + 2 * 365 * cert_params[:cert_validity].to_i * 60 * 60 # 2 years validity - ef = OpenSSL::X509::ExtensionFactory.new - ef.subject_certificate = cert - ef.issuer_certificate = cert - cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false)) - cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false)) - cert.add_extension(ef.create_extension("extendedKeyUsage", "1.3.6.1.5.5.7.3.1", false)) - cert.sign(rsa_key, OpenSSL::Digest.new("SHA1")) - @thumbprint = OpenSSL::Digest::SHA1.new(cert.to_der) - cert - end - - def write_certificate_to_file(cert, file_path, rsa_key, cert_params) - File.open(file_path + ".pem", "wb") { |f| f.print cert.to_pem } - @winrm_cert_passphrase = prompt_for_passphrase unless @winrm_cert_passphrase - pfx = OpenSSL::PKCS12.create("#{cert_params[:winrm_cert_passphrase]}", "winrmcert", rsa_key, cert) - File.open(file_path + ".pfx", "wb") { |f| f.print pfx.to_der } - File.open(file_path + ".b64", "wb") { |f| f.print Base64.strict_encode64(pfx.to_der) } - end - - ########## SSL certificate generation ends ########### - - end -end diff --git a/lib/azure/service_management/connection.rb b/lib/azure/service_management/connection.rb deleted file mode 100644 index 2fc91262..00000000 --- a/lib/azure/service_management/connection.rb +++ /dev/null @@ -1,102 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "image" -require_relative "role" -require_relative "deploy" -require_relative "host" -require_relative "loadbalancer" -require_relative "vnet" -require_relative "utility" -require_relative "ag" -require_relative "storageaccount" -require_relative "certificate" -require_relative "disk" - -module Azure - class ServiceManagement - class Connection - include AzureUtility - attr_accessor :hosts, :rest, :images, :deploys, :roles, - :disks, :storageaccounts, :certificates, :ags, :vnets, :lbs - def initialize(rest) - @images = Images.new(self) - @roles = Roles.new(self) - @deploys = Deploys.new(self) - @hosts = Hosts.new(self) - @rest = rest - @lbs = Loadbalancer.new(self) - @vnets = Vnets.new(self) - @ags = AGs.new(self) - @storageaccounts = StorageAccounts.new(self) - @certificates = Certificates.new(self) - @disks = Disks.new(self) - end - - def query_azure(service_name, - verb = "get", - body = "", - params = "", - wait = true, - services = true, - content_type = nil) - Chef::Log.info "calling " + verb + " " + service_name + (wait ? " synchronously" : " asynchronously") - Chef::Log.debug body unless body == "" - response = @rest.query_azure(service_name, verb, body, params, services, content_type) - if response.code.to_i == 200 - ret_val = Nokogiri::XML response.body - elsif !wait && response.code.to_i == 202 - Chef::Log.debug "Request accepted in asynchronous mode" - ret_val = Nokogiri::XML response.body - elsif response.code.to_i >= 201 && response.code.to_i <= 299 - ret_val = wait_for_completion - else - if response.body - ret_val = Nokogiri::XML response.body - Chef::Log.debug ret_val.to_xml - error_code, error_message = error_from_response_xml(ret_val) - Chef::Log.debug error_code + " : " + error_message if error_code.length > 0 - else - Chef::Log.warn "http error: " + response.code - end - end - ret_val - end - - def wait_for_completion - status = "InProgress" - Chef::Log.info "Waiting while status returns InProgress" - while status == "InProgress" - response = @rest.query_for_completion - ret_val = Nokogiri::XML response.body - status = xml_content(ret_val, "Status") - if status == "InProgress" - print "." - sleep(0.5) - elsif status == "Succeeded" - Chef::Log.debug "not InProgress : " + ret_val.to_xml - else - error_code, error_message = error_from_response_xml(ret_val) - Chef::Log.debug status + error_code + " : " + error_message if error_code.length > 0 - end - end - ret_val - end - end - end -end diff --git a/lib/azure/service_management/deploy.rb b/lib/azure/service_management/deploy.rb deleted file mode 100755 index 85eac0a2..00000000 --- a/lib/azure/service_management/deploy.rb +++ /dev/null @@ -1,221 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class Deploys - include AzureUtility - def initialize(connection) - @connection = connection - end - - # force_load should be true when there is something in local cache and we want to reload - # first call is always load. - def load(force_load = false) - unless @deploys || force_load - @deploys = begin - deploys = [] - hosts = @connection.hosts.all - hosts.each do |host| - deploy = Deploy.new(@connection) - deploy.retrieve(host.name) - if deploy.name - host.add_deploy(deploy) - deploys << deploy - end - end - deploys - end - end - @deploys - end - - def all - load - end - - # TODO - Current knife-azure plug-in seems to have assumption that single hostedservice - # will always have one deployment (production). see Deploy#retrieve below - def get_deploy_name_for_hostedservice(hostedservicename) - host = @connection.hosts.find(hostedservicename) - if host && host.deploys.length > 0 - host.deploys[0].name - else - nil - end - end - - def create(params) - if params[:azure_connect_to_existing_dns] - unless @connection.hosts.exists?(params[:azure_dns_name]) - Chef::Log.fatal "The specified Azure DNS Name does not exist." - exit 1 - end - else - ret_val = @connection.hosts.create(params) - error_code, error_message = error_from_response_xml(ret_val) - if error_code.length > 0 - Chef::Log.fatal "Unable to create DNS:" + error_code + " : " + error_message - exit 1 - end - end - unless @connection.storageaccounts.exists?(params[:azure_storage_account]) - @connection.storageaccounts.create(params) - end - if params[:ssh_identity_file] - params[:fingerprint] = @connection.certificates.create(params) - end - if params[:cert_path] - cert_data = File.read (params[:cert_path]) - @connection.certificates.add cert_data, params[:cert_password], "pfx", params[:azure_dns_name] - elsif params[:winrm_ssl] - # TODO: generate certificates for ssl listener - end - - params["deploy_name"] = get_deploy_name_for_hostedservice(params[:azure_dns_name]) - - if !params["deploy_name"].nil? - role = Role.new(@connection) - roleXML = role.setup(params) - ret_val = role.create(params, roleXML) - else - params["deploy_name"] = params[:azure_dns_name] - deploy = Deploy.new(@connection) - deployXML = deploy.setup(params) - ret_val = deploy.create(params, deployXML) - end - error_code, error_message = error_from_response_xml(ret_val) - if error_code.length > 0 - Chef::Log.debug(ret_val.to_s) - raise Chef::Log.fatal "Unable to create role:" + error_code + " : " + error_message - end - @connection.roles.find_in_hosted_service(params[:azure_vm_name], params[:azure_dns_name]) - end - - def delete(rolename); end - - def queryDeploy(hostedservicename) - deploy = Deploy.new(@connection) - deploy.retrieve(hostedservicename) - deploy - end - end - - class Deploy - include AzureUtility - attr_accessor :connection, :name, :status, :url, :hostedservicename, :input_endpoints, :loadbalancers - - def initialize(connection) - @connection = connection - end - - def retrieve(hostedservicename) - @hostedservicename = hostedservicename - deployXML = @connection.query_azure("hostedservices/#{hostedservicename}/deploymentslots/Production") - if deployXML.at_css("Deployment Name") != nil - @name = xml_content(deployXML, "Deployment Name") - @status = xml_content(deployXML, "Deployment Status") - @url = xml_content(deployXML, "Deployment Url") - @roles = {} - rolesXML = deployXML.css("Deployment RoleInstanceList RoleInstance") - rolesListXML = deployXML.css("Deployment RoleList Role") - rolesXML.zip(rolesListXML).each do |roleXML, roleListXML| - role = Role.new(@connection) - role.parse(roleXML, hostedservicename, @name) - if role.publicipaddress.to_s.empty? - role.publicipaddress = xml_content(deployXML, "VirtualIPs VirtualIP Address") - end - role.parse_role_list_xml(roleListXML) - @roles[role.name] = role - end - @input_endpoints = [] - endpointsXML = deployXML.css("InputEndpoint") - endpointsXML.each do |endpointXML| - @input_endpoints << parse_endpoint(endpointXML) - end - @loadbalancers = {} - lbsXML = deployXML.css("Deployment LoadBalancers LoadBalancer") - lbsXML.each do |lbXML| - loadbalancer = Loadbalancer.new(@connection) - loadbalancer.parse(lbXML, hostedservicename) - @loadbalancers[loadbalancer.name] = loadbalancer - end - end - end - - def setup(params) - role = Role.new(@connection) - roleXML = role.setup(params) - builder = Nokogiri::XML::Builder.new do |xml| - xml.Deployment( - "xmlns" => "http://schemas.microsoft.com/windowsazure", - "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance" - ) do - xml.Name params["deploy_name"] - xml.DeploymentSlot "Production" - xml.Label Base64.encode64(params["deploy_name"]).strip - xml.RoleList { xml.Role("i:type" => "PersistentVMRole") } - if params[:azure_network_name] - xml.VirtualNetworkName params[:azure_network_name] - end - end - end - builder.doc.at_css("Role") << roleXML.at_css("PersistentVMRole").children.to_s - builder.doc - end - - def create(params, deployXML) - servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments" - @connection.query_azure(servicecall, "post", deployXML.to_xml) - end - - # This parses endpoints from a RoleList-Role-InputEndpoint, NOT a RoleInstanceList-RoleInstance-InstanceEndpoint - # Refactor: make this an object rather than a hash..? - def parse_endpoint(inputendpoint_xml) - hash = {} - %w{LoadBalancedEndpointSetName LocalPort Name Port Protocol EnableDirectServerReturn LoadBalancerName IdleTimeoutInMinutes}.each do |key| - hash[key] = xml_content(inputendpoint_xml, key, nil) - end - # Protocol could be in there twice... If we have two, pick the second one as the first is for the probe. - if inputendpoint_xml.css("Protocol").count > 1 - hash["Protocol"] = inputendpoint_xml.css("Protocol")[1].content - end - probe = inputendpoint_xml.css("LoadBalancerProbe") - if probe - hash["LoadBalancerProbe"] = {} - %w{Path Port Protocol IntervalInSeconds TimeoutInSeconds}.each do |key| - hash["LoadBalancerProbe"][key] = xml_content(probe, key, nil) - end - end - hash - end - - def roles - @roles.values if @roles - end - - # just delete from local cache - def delete_role_if_present(role) - @roles.delete(role.name) if @roles - end - - def find_role(name) - @roles[name] if @roles - end - - end -end diff --git a/lib/azure/service_management/disk.rb b/lib/azure/service_management/disk.rb deleted file mode 100755 index 6b8ee4fe..00000000 --- a/lib/azure/service_management/disk.rb +++ /dev/null @@ -1,68 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class Disks - def initialize(connection) - @connection = connection - end - - def all - disks = [] - response = @connection.query_azure("disks") - founddisks = response.css("Disk") - founddisks.each do |disk| - item = Disk.new(disk) - disks << item - end - disks - end - - def find(name) - founddisk = nil - all.each do |disk| - next unless disk.name == name - - founddisk = disk - end - founddisk - end - - def exists(name) - !find(name).nil? - end - - def clear_unattached - all.each do |disk| - next unless disk.attached == false - - @connection.query_azure("disks/" + disk.name, "delete") - end - end - end -end - -module Azure - class Disk - attr_accessor :name, :attached - def initialize(disk) - @name = disk.at_css("Name").content - @attached = !disk.at_css("AttachedTo").nil? - end - end -end diff --git a/lib/azure/service_management/host.rb b/lib/azure/service_management/host.rb deleted file mode 100755 index fbc95f08..00000000 --- a/lib/azure/service_management/host.rb +++ /dev/null @@ -1,184 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class Hosts - include AzureUtility - def initialize(connection) - @connection = connection - end - - # force_load should be true when there is something in local cache and we want to reload - # first call is always load. - def load(force_load = false) - unless @hosted_services || force_load - @hosted_services = begin - hosted_services = {} - responseXML = @connection.query_azure("hostedservices") - servicesXML = responseXML.css("HostedServices HostedService") - servicesXML.each do |serviceXML| - host = Host.new(@connection).parse(serviceXML) - hosted_services[host.name] = host - end - hosted_services - end - end - @hosted_services - end - - def all - load.values - end - - # first look up local cache if we have already loaded list. - def exists?(name) - return @hosted_services.key?(name) if @hosted_services - - exists_on_cloud?(name) - end - - # Look up on cloud and not local cache - def exists_on_cloud?(name) - ret_val = @connection.query_azure("hostedservices/#{name}") - error_code, error_message = error_from_response_xml(ret_val) if ret_val - if ret_val.nil? || error_code.length > 0 - Chef::Log.debug("Unable to find hosted(cloud) service:" + error_code + " : " + error_message) if ret_val - false - else - true - end - end - - # first look up local cache if we have already loaded list. - def find(name) - return @hosted_services[name] if @hosted_services && @hosted_services.key?(name) - - fetch_from_cloud(name) - end - - # Look up hosted service on cloud and not local cache - def fetch_from_cloud(name) - ret_val = @connection.query_azure("hostedservices/#{name}") - error_code, error_message = error_from_response_xml(ret_val) if ret_val - if ret_val.nil? || error_code.length > 0 - Chef::Log.warn("Unable to find hosted(cloud) service:" + error_code + " : " + error_message) if ret_val - nil - else - Host.new(@connection).parse(ret_val) - end - end - - def create(params) - host = Host.new(@connection) - host.create(params) - end - - def delete(name) - if exists?(name) - servicecall = "hostedservices/" + name - @connection.query_azure(servicecall, "delete") - end - end - end -end - -module Azure - class Host - include AzureUtility - attr_accessor :connection, :name, :url, :label - attr_accessor :dateCreated, :description, :location - attr_accessor :dateModified, :status - - def initialize(connection) - @connection = connection - @deploys_loaded = false - @deploys = {} - end - - def parse(serviceXML) - @name = xml_content(serviceXML, "ServiceName") - @url = xml_content(serviceXML, "Url") - @label = xml_content(serviceXML, "HostedServiceProperties Label") - @dateCreated = xml_content(serviceXML, "HostedServiceProperties DateCreated") - @description = xml_content(serviceXML, "HostedServiceProperties Description") - @location = xml_content(serviceXML, "HostedServiceProperties Location") - @dateModified = xml_content(serviceXML, "HostedServiceProperties DateLastModified") - @status = xml_content(serviceXML, "HostedServiceProperties Status") - self - end - - def create(params) - builder = Nokogiri::XML::Builder.new do |xml| - xml.CreateHostedService("xmlns" => "http://schemas.microsoft.com/windowsazure") do - xml.ServiceName params[:azure_dns_name] - xml.Label Base64.encode64(params[:azure_dns_name]) - xml.Description "Explicitly created hosted service" - unless params[:azure_service_location].nil? - xml.Location params[:azure_service_location] - end - unless params[:azure_affinity_group].nil? - xml.AffinityGroup params[:azure_affinity_group] - end - end - end - @connection.query_azure("hostedservices", "post", builder.to_xml) - end - - def details - response = @connection.query_azure("hostedservices/" + @name + "?embed-detail=true") - end - - # Deployments within this hostedservice - def add_deploy(deploy) - @deploys[deploy.name] = deploy - end - - def delete_role(role) - deploys.each { |d| d.delete_role_if_present(role) } - end - - def deploys - # check if we have deploys loaded, else load. - if (@deploys.length == 0) && !@deploys_loaded - deploy = Deploy.new(@connection) - deploy.retrieve(@name) - @deploys[deploy.name] = deploy - @deploys_loaded = true - end - @deploys.values - end - - def roles - roles = [] - deploys.each do |deploy| - roles.concat(deploy.roles) if deploy.roles - end - roles - end - - def find_role(role_name, deploy_name = nil) - return @deploys[deploy_name].find_role(role_name) if deploy_name && deploys - - # else lookup all deploys within hostedservice - deploys.each do |deploy| - role = deploy.find_role(role_name) - return role if role - end - end - end -end diff --git a/lib/azure/service_management/image.rb b/lib/azure/service_management/image.rb deleted file mode 100755 index 4943e890..00000000 --- a/lib/azure/service_management/image.rb +++ /dev/null @@ -1,94 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class Images - def initialize(connection) - @connection = connection - end - - def load - @images ||= begin - osimages = get_images("OSImage") # get OSImages - vmimages = get_images("VMImage") # get VMImages - - all_images = osimages.merge(vmimages) - end - end - - def all - load.values - end - - # img_type = OSImages or VMImage - def get_images(img_type) - images = {} - - if img_type == "OSImage" - response = @connection.query_azure("images") - elsif img_type == "VMImage" - response = @connection.query_azure("vmimages") - end - - unless response.to_s.empty? - osimages = response.css(img_type) - - osimages.each do |image| - item = Image.new(image) - images[item.name] = item - end - end - - images - end - - def is_os_image(image_name) - os_images = get_images("OSImage").values - os_images.detect { |img| img.name == image_name } ? true : false - end - - def is_vm_image(image_name) - vm_images = get_images("VMImage").values - vm_images.detect { |img| img.name == image_name } ? true : false - end - - def exists?(name) - all.detect { |img| img.name == name } ? true : false - end - - def find(name) - load[name] - end - end -end - -module Azure - class Image - attr_accessor :category, :label - attr_accessor :name, :os, :eula, :description, :location - def initialize(image) - @category = image.at_css("Category").content - @label = image.at_css("Label").content - @name = image.at_css("Name").content - @os = image.at_css("OS").content - @location = image.at_css("Location").content.gsub(";", ", ") if image.at_css("Location") - @eula = image.at_css("Eula").content if image.at_css("Eula") - @description = image.at_css("Description").content if image.at_css("Description") - end - end -end diff --git a/lib/azure/service_management/loadbalancer.rb b/lib/azure/service_management/loadbalancer.rb deleted file mode 100644 index 6d86ce9d..00000000 --- a/lib/azure/service_management/loadbalancer.rb +++ /dev/null @@ -1,78 +0,0 @@ -# -# Author:: Aiman Alsari (aiman.alsari@gmail.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class Loadbalancer - include AzureUtility - attr_accessor :name, :service, :subnet, :vip - - def initialize(connection) - @connection = connection - end - - def load - @lbs ||= begin - @lbs = {} - @connection.deploys.all.each do |deploy| - @lbs.merge!(deploy.loadbalancers) - end - @lbs - end - end - - def all - load.values - end - - def exists?(name) - load.key?(name) - end - - def find(name) - load[name] - end - - def parse(lbXML, hostedservicename) - @name = xml_content(lbXML, "Name") - ip_configXML = lbXML.css("FrontendIpConfiguration") - @subnet = xml_content(ip_configXML, "SubnetName") - @vip = xml_content(ip_configXML, "StaticVirtualNetworkIPAddress") - @service = hostedservicename - self - end - - def create(params) - if params[:azure_lb_static_vip] && !params[:azure_subnet_name] - Chef::Log.fatal "Unable to create Loadbalancer, :azure_subnet_name needs to be set if :azure_lb_static_vip is set" - end - builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml| - xml.LoadBalancer(xmlns: "http://schemas.microsoft.com/windowsazure") do - xml.Name params[:azure_load_balancer] - xml.FrontendIpConfiguration do - xml.Type "Private" - xml.SubnetName params[:azure_subnet_name] if params[:azure_subnet_name] - xml.StaticVirtualNetworkIPAddress params[:azure_lb_static_vip] if params[:azure_lb_static_vip] - end - end - end - deploy_name = @connection.deploys.get_deploy_name_for_hostedservice(params[:azure_dns_name]) - servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments/#{deploy_name}/loadbalancers" - @connection.query_azure(servicecall, "post", builder.doc.to_xml) - end - end -end diff --git a/lib/azure/service_management/rest.rb b/lib/azure/service_management/rest.rb deleted file mode 100755 index cbf9630a..00000000 --- a/lib/azure/service_management/rest.rb +++ /dev/null @@ -1,126 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require "net/http" unless defined?(Net::HTTP) -require "openssl" unless defined?(OpenSSL) -require "uri" unless defined?(URI) -require "nokogiri" unless defined?(Nokogiri) - -module AzureAPI - - class Rest - def initialize(params) - @subscription_id = params[:azure_subscription_id] - @pem_file = params[:azure_mgmt_cert] - @host_name = params[:azure_api_host_name] - @verify_ssl = params[:verify_ssl_cert] - end - - def query_azure(service_name, - verb = "get", - body = "", - params = "", - services = true, - content_type = nil) - svc_str = services ? "/services" : "" - uri = URI.parse("#{@host_name}/#{@subscription_id}#{svc_str}/#{service_name}") - scheme = !uri.scheme ? "https://" : "" - request_url = "#{scheme}#{@host_name}/#{@subscription_id}#{svc_str}/#{service_name}" - print "." - response = http_query(request_url, verb, body, params, content_type) - if response.code.to_i == 307 - Chef::Log.debug "Redirect to #{response["Location"]}" - response = http_query(response["Location"], verb, body, params, content_type) - end - @last_request_id = response["x-ms-request-id"] - response - end - - def http_query(request_url, verb, body, params, content_type = nil) - uri = URI.parse(request_url) - uri.query = params - http = http_setup(uri) - request = request_setup(uri, verb, body, content_type) - response = http.request(request) - @last_request_id = response["x-ms-request-id"] - response - end - - def query_for_completion - uri = URI.parse("#{@host_name}/#{@subscription_id}/operations/#{@last_request_id}") - scheme = !uri.scheme ? "https://" : "" - request_url = "#{scheme}#{@host_name}/#{@subscription_id}/operations/#{@last_request_id}" - response = http_query(request_url, "get", "", "") - if response.code.to_i == 307 - Chef::Log.debug "Redirect to #{response["Location"]}" - response = http_query(response["Location"], "get", "", "") - end - response - end - - def http_setup(uri) - http = Net::HTTP.new(uri.host, uri.port) - store = OpenSSL::X509::Store.new - store.set_default_paths - http.cert_store = store - if @verify_ssl - http.verify_mode = OpenSSL::SSL::VERIFY_PEER - else - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - end - http.use_ssl = true - begin - http.cert = OpenSSL::X509::Certificate.new(@pem_file) - rescue OpenSSL::X509::CertificateError => err - raise "Invalid Azure Certificate pem file. Error: #{err}" - end - http.key = OpenSSL::PKey::RSA.new(@pem_file) - http - end - - def request_setup(uri, verb, body, content_type) - if verb == "get" - request = Net::HTTP::Get.new(uri.request_uri) - elsif verb == "post" - request = Net::HTTP::Post.new(uri.request_uri) - elsif verb == "delete" - request = Net::HTTP::Delete.new(uri.request_uri) - elsif verb == "put" - request = Net::HTTP::Put.new(uri.request_uri) - end - text = verb == "put" && content_type.nil? - request["x-ms-version"] = "2014-05-01" - request["content-type"] = text ? "text/plain" : "application/xml" - request["accept"] = "application/xml" - request["accept-charset"] = "utf-8" - request.body = body - request - end - - def showResponse(response) - puts "=== response body ===" - puts response.body - puts "=== response.code ===" - puts response.code - puts "=== response.inspect ===" - puts response.inspect - puts "=== all of the headers ===" - puts response.each_header { |h, j| puts h.inspect + " : " + j.inspect } - end - end -end diff --git a/lib/azure/service_management/role.rb b/lib/azure/service_management/role.rb deleted file mode 100644 index e897bb2c..00000000 --- a/lib/azure/service_management/role.rb +++ /dev/null @@ -1,717 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require "securerandom" unless defined?(SecureRandom) -require_relative "utility" - -module Azure - class Roles - include AzureUtility - attr_accessor :connection, :roles - def initialize(connection) - @connection = connection - @roles = nil - end - - # do not use this unless you want a list of all roles(vms) in your subscription - def all - @roles = [] - @connection.deploys.all.each do |deploy| - deploy.roles.each do |role| - @roles << role - end - end - @roles - end - - def find_roles_within_hostedservice(hostedservicename) - host = @connection.hosts.find(hostedservicename) - host ? host.roles : nil # nil says invalid hosted service - end - - def find_in_hosted_service(role_name, hostedservicename) - host = @connection.hosts.find(hostedservicename) - return nil if host.nil? - - host.find_role(role_name) - end - - def find(role_name, params = nil) - if params && params[:azure_dns_name] - return find_in_hosted_service(role_name, params[:azure_dns_name]) - end - - all if @roles.nil? - - # TODO: - optimize this lookup - @roles.each do |role| - return role if role.name == role_name - end - nil - end - - def alone_on_hostedservice(found_role) - roles = find_roles_within_hostedservice(found_role.hostedservicename) - return false if roles && roles.length > 1 - - true - end - - def exists?(name) - find(name) != nil - end - - def delete(params) - role = find(params[:name]) - unless role.nil? - roleXML = nil - roleXML = @connection.query_azure("hostedservices/#{role.hostedservicename}", "get", "", "embed-detail=true") - osdisk = roleXML.css(roleXML, "OSVirtualHardDisk") - disk_name = xml_content(osdisk, "DiskName") - storage_account_name = xml_content(osdisk, "MediaLink").gsub("http://", "").gsub(/.blob(.*)$/, "") - - if !params[:preserve_azure_os_disk] && !params[:preserve_azure_vhd] && !params[:wait] - # default compmedia = true. So, it deletes role and associated resources - check_and_delete_role_and_resources(params, role) - else - # compmedia = false. So, it deletes only role and not associated resources - check_and_delete_role_and_resources(params, role, compmedia = false) - check_and_delete_disks(params, disk_name) - check_and_delete_service(params) - end - check_and_delete_storage(params, disk_name, storage_account_name) - end - end - - def check_and_delete_role_and_resources(params, role, compmedia = true) - if alone_on_hostedservice(role) - if !params[:preserve_azure_dns_name] && compmedia - servicecall = "hostedservices/#{role.hostedservicename}" - else - servicecall = "hostedservices/#{role.hostedservicename}/deployments/#{role.deployname}" - end - else - servicecall = "hostedservices/#{role.hostedservicename}/deployments" \ - "/#{role.deployname}/roles/#{role.name}" - end - if compmedia - @connection.query_azure(servicecall, "delete", "", "comp=media", wait = params[:wait]) - else - @connection.query_azure(servicecall, "delete") - end - - # delete role from local cache as well. - @connection.hosts.find(role.hostedservicename).delete_role(role) - @roles.delete(role) if @roles - end - - def check_and_delete_disks(params, disk_name) - servicecall = "disks/#{disk_name}" - unless params[:preserve_azure_os_disk] - # OS Disk can only be deleted if it is detached from the VM. - # So Iteratively check for disk detachment from the VM while waiting for 5 minutes , - # exit otherwise after 12 attempts. - for attempt in 0..12 - break if @connection.query_azure(servicecall, "get").search("AttachedTo").text == "" - - attempt == 12 ? (puts "The associated disk could not be deleted due to time out.") : (sleep 25) - end - if params[:preserve_azure_vhd] - @connection.query_azure(servicecall, "delete") - else - @connection.query_azure(servicecall, "delete", "", "comp=media", wait = params[:wait]) - end - end - end - - def check_and_delete_service(params) - unless params[:preserve_azure_dns_name] - unless params[:azure_dns_name].nil? - roles_using_same_service = find_roles_within_hostedservice(params[:azure_dns_name]) - if roles_using_same_service.size <= 1 - servicecall = "hostedservices/" + params[:azure_dns_name] - @connection.query_azure(servicecall, "delete") - end - end - end - end - - def check_and_delete_storage(params, disk_name, storage_account_name) - if params[:delete_azure_storage_account] - # Iteratively check for disk deletion - for attempt in 0..12 - break unless @connection.query_azure("disks").search("Name").text.include?(disk_name) - - attempt == 12 ? (puts "The associated disk could not be deleted due to time out.") : (sleep 25) - end - begin - @connection.query_azure("storageservices/#{storage_account_name}", "delete") - rescue Exception => ex - ui.warn(ex.message.to_s) - ui.warn(ex.backtrace.join("\n").to_s) - end - end - end - - def update(name, params) - role = Role.new(@connection) - roleExtensionXml = role.setup_extension(params) - role.update(name, params, roleExtensionXml) - end - - private :check_and_delete_role_and_resources, :check_and_delete_disks, :check_and_delete_service, :check_and_delete_storage - end - - class Role - include AzureUtility - attr_accessor :connection, :name, :status, :size, :ipaddress, :publicipaddress - attr_accessor :sshport, :hostedservicename, :deployname, :thumbprint - attr_accessor :winrmport - attr_accessor :hostname, :tcpports, :udpports - attr_accessor :role_xml, :os_type, :os_version - - TCP_ENDPOINTS_MAPPING = { "3389" => "Remote Desktop", - "5986" => "PowerShell", - "22" => "SSH", - "21" => "FTP", - "25" => "SMTP", - "53" => "DNS", - "80" => "HTTP", - "110" => "POP3", - "143" => "IMAP", - "389" => "LDAP", - "443" => "HTTPs", - "587" => "SMTPS", - "995" => "POP3S", - "993" => "IMAPS", - "1433" => "MSSQL", - "3306" => "MySQL" }.freeze - - def initialize(connection) - @connection = connection - end - - def parse(roleXML, hostedservicename, deployname) - @name = xml_content(roleXML, "RoleName") - @status = xml_content(roleXML, "InstanceStatus") - @size = xml_content(roleXML, "InstanceSize") - @ipaddress = xml_content(roleXML, "IpAddress") - @hostname = xml_content(roleXML, "HostName") - @hostedservicename = hostedservicename - @deployname = deployname - @thumbprint = fetch_thumbprint - @tcpports = [] - @udpports = [] - - endpoints = roleXML.css("InstanceEndpoint") - @publicipaddress = xml_content(endpoints[0], "Vip") unless endpoints.empty? - endpoints.each do |endpoint| - if xml_content(endpoint, "Name").casecmp("ssh").zero? - @sshport = xml_content(endpoint, "PublicPort") - elsif xml_content(endpoint, "Name").casecmp("winrm").zero? - @winrmport = xml_content(endpoint, "PublicPort") - else - hash = {} - hash["Name"] = xml_content(endpoint, "Name") - hash["Vip"] = xml_content(endpoint, "Vip") - hash["PublicPort"] = xml_content(endpoint, "PublicPort") - hash["LocalPort"] = xml_content(endpoint, "LocalPort") - - if xml_content(endpoint, "Protocol") == "tcp" - @tcpports << hash - else # == 'udp' - @udpports << hash - end - end - end - end - - def parse_role_list_xml(roleListXML) - @role_xml = roleListXML - os_disk_xml = roleListXML.css("OSVirtualHardDisk") - @os_type = xml_content(os_disk_xml, "OS") - @os_version = xml_content(os_disk_xml, "SourceImageName") - end - - # Expects endpoint_param_string to be in the form {localport}:{publicport}:{lb_set_name}:{lb_probe_path} - # Only localport is mandatory. - def parse_endpoint_from_params(protocol, _azure_vm_name, endpoint_param_string) - fields = endpoint_param_string.split(":").map(&:strip) - hash = {} - hash["LocalPort"] = fields[0] - hash["Port"] = fields[1] || fields[0] - hash["LoadBalancerName"] = fields[2] if fields[2] != "EXTERNAL" # TODO: hackity hack.. Shouldn't use magic words. - hash["LoadBalancedEndpointSetName"] = fields[3] - hash["Protocol"] = protocol - if TCP_ENDPOINTS_MAPPING.include?(hash["Port"]) && protocol == "TCP" - hash["Name"] = TCP_ENDPOINTS_MAPPING[hash["Port"]] - else - hash["Name"] = "#{protocol}Endpoint_chef_#{fields[0]}" - end - if fields[2] - hash["LoadBalancerProbe"] = {} - hash["LoadBalancerProbe"]["Path"] = fields[4] - hash["LoadBalancerProbe"]["Port"] = fields[0] - hash["LoadBalancerProbe"]["Protocol"] = fields[4] ? "HTTP" : protocol - end - hash - end - - def find_deploy(params) - @connection.hosts.find(params[:azure_dns_name]).deploys[0] # TODO: this relies on the 'production only' bug. - end - - def add_endpoints_to_xml(xml, endpoints, params) - existing_endpoints = find_deploy(params).input_endpoints - - endpoints.each do |ep| - if existing_endpoints - existing_endpoints.each do |eep| - ep = eep if eep["LoadBalancedEndpointSetName"] && ep["LoadBalancedEndpointSetName"] && (eep["LoadBalancedEndpointSetName"] == ep["LoadBalancedEndpointSetName"]) - end - end - - if ep["Port"] == params[:port] && ep["Protocol"].casecmp("tcp").zero? - puts("Skipping tcp-endpoints: #{ep["LocalPort"]} because this port is already in use by ssh/winrm endpoint in current VM.") - next - end - - xml.InputEndpoint do - xml.LoadBalancedEndpointSetName ep["LoadBalancedEndpointSetName"] if ep["LoadBalancedEndpointSetName"] - xml.LocalPort ep["LocalPort"] - xml.Name ep["Name"] - xml.Port ep["Port"] - if ep["LoadBalancerProbe"] - xml.LoadBalancerProbe do - xml.Path ep["LoadBalancerProbe"]["Path"] if ep["LoadBalancerProbe"]["Path"] - xml.Port ep["LoadBalancerProbe"]["Port"] - xml.Protocol ep["LoadBalancerProbe"]["Protocol"] - xml.IntervalInSeconds ep["LoadBalancerProbe"]["IntervalInSeconds"] if ep["LoadBalancerProbe"]["IntervalInSeconds"] - xml.TimeoutInSeconds ep["LoadBalancerProbe"]["TimeoutInSeconds"] if ep["LoadBalancerProbe"]["TimeoutInSeconds"] - end - end - xml.Protocol ep["Protocol"] - xml.EnableDirectServerReturn ep["EnableDirectServerReturn"] if ep["EnableDirectServerReturn"] - xml.LoadBalancerName ep["LoadBalancerName"] if ep["LoadBalancerName"] - xml.IdleTimeoutInMinutes ep["IdleTimeoutInMinutes"] if ep["IdleTimeoutInMinutes"] - end - end - end - - def fetch_thumbprint - query_result = connection.query_azure("hostedservices/#{@hostedservicename}/deployments/#{@hostedservicename}/roles/#{@name}") - query_result.at_css("DefaultWinRmCertificateThumbprint").nil? ? "" : query_result.at_css("DefaultWinRmCertificateThumbprint").text - end - - def setup(params) - azure_user_domain_name = params[:azure_user_domain_name] || params[:azure_domain_name] - builder = Nokogiri::XML::Builder.new do |xml| - xml.PersistentVMRole( - "xmlns" => "http://schemas.microsoft.com/windowsazure", - "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance" - ) do - xml.RoleName { xml.text params[:azure_vm_name] } - xml.OsVersion("i:nil" => "true") - xml.RoleType "PersistentVMRole" - - xml.ConfigurationSets do - if params[:os_type] == "Linux" - xml.ConfigurationSet("i:type" => "LinuxProvisioningConfigurationSet") do - xml.ConfigurationSetType "LinuxProvisioningConfiguration" - xml.HostName params[:azure_vm_name] - xml.UserName params[:connection_user] - if params[:ssh_identity_file].nil? - xml.UserPassword params[:connection_password] - xml.DisableSshPasswordAuthentication "false" - else - xml.DisableSshPasswordAuthentication "true" - xml.SSH do - xml.PublicKeys do - xml.PublicKey do - xml.Fingerprint params[:fingerprint].to_s.upcase - xml.Path "/home/" + params[:connection_user] + "/.ssh/authorized_keys" - end - end - end - end - end - elsif params[:os_type] == "Windows" - xml.ConfigurationSet("i:type" => "WindowsProvisioningConfigurationSet") do - xml.ConfigurationSetType "WindowsProvisioningConfiguration" - xml.ComputerName params[:azure_vm_name] - xml.AdminPassword params[:admin_password] - xml.ResetPasswordOnFirstLogon "false" - xml.EnableAutomaticUpdates "false" - if params[:azure_domain_name] - xml.DomainJoin do - xml.Credentials do - xml.Domain azure_user_domain_name - xml.Username params[:azure_domain_user] - xml.Password params[:azure_domain_passwd] - end - xml.JoinDomain params[:azure_domain_name] - xml.MachineObjectOU params[:azure_domain_ou_dn] if params[:azure_domain_ou_dn] - end - end - if params[:connection_protocol].casecmp("winrm").zero? - if params[:ssl_cert_fingerprint] - xml.StoredCertificateSettings do - xml.CertificateSetting do - xml.StoreLocation "LocalMachine" - xml.StoreName "My" - xml.Thumbprint params[:ssl_cert_fingerprint] - end - end - end - xml.WinRM do - xml.Listeners do - if params[:winrm_ssl] || params[:ssl_cert_fingerprint] - xml.Listener do - xml.CertificateThumbprint params[:ssl_cert_fingerprint] if params[:ssl_cert_fingerprint] - xml.Protocol "Https" - end - else - xml.Listener do - xml.Protocol "Http" - end - end - end - end - end - xml.AdminUsername params[:connection_user] - if params[:connection_protocol].casecmp("winrm").zero? && (params[:winrm_max_timeout] || params[:winrm_max_memory_per_shell]) - xml.AdditionalUnattendContent do - xml.Passes do - xml.UnattendPass do - xml.PassName "oobeSystem" - xml.Components do - xml.UnattendComponent do - xml.ComponentName "Microsoft-Windows-Shell-Setup" - xml.ComponentSettings do - xml.ComponentSetting do - xml.SettingName "AutoLogon" - xml.Content Base64.encode64( - Nokogiri::XML::Builder.new do |auto_logon_xml| - auto_logon_xml.AutoLogon do - auto_logon_xml.Username params[:connection_user] - auto_logon_xml.Password do - auto_logon_xml.Value params[:admin_password] - auto_logon_xml.PlainText true - end - auto_logon_xml.LogonCount 1 - auto_logon_xml.Enabled true - end - end.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION) - ).strip - end - xml.ComponentSetting do - xml.SettingName "FirstLogonCommands" - xml.Content Base64.encode64( - Nokogiri::XML::Builder.new do |first_logon_xml| - first_logon_xml.FirstLogonCommands do - if params[:winrm_max_timeout] - first_logon_xml.SynchronousCommand("wcm:action" => "add") do - first_logon_xml.Order 1 - first_logon_xml.CommandLine "cmd.exe /c winrm set winrm/config @{MaxTimeoutms=\"#{params[:winrm_max_timeout]}\"}" - first_logon_xml.Description "Bump WinRM max timeout to #{params[:winrm_max_timeout]} milliseconds" - end - end - - if params[:winrm_max_memory_per_shell] - first_logon_xml.SynchronousCommand("wcm:action" => "add") do - first_logon_xml.Order 2 - first_logon_xml.CommandLine "cmd.exe /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB=\"#{params[:winrm_max_memory_per_shell]}\"}" - first_logon_xml.Description "Bump WinRM max memory per shell to #{params[:winrm_max_memory_per_shell]} MB" - end - end - end - end.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION) - ).strip - end - end - end - end - end - end - end - end - end - end - - xml.ConfigurationSet("i:type" => "NetworkConfigurationSet") do - xml.ConfigurationSetType "NetworkConfiguration" - xml.InputEndpoints do - # 1. connection_protocol = 'winrm' for windows => Set winrm port - # 2. connection_protocol = 'ssh' for windows and linux => Set ssh port - # 3. connection_protocol = 'cloud-api' for windows and linux => Set no port - if (params[:os_type] == "Windows") && params[:connection_protocol].casecmp("winrm").zero? - xml.InputEndpoint do - if params[:winrm_ssl] - xml.LocalPort "5986" - else - xml.LocalPort "5985" - end - xml.Name "WinRM" - xml.Port params[:port] - xml.Protocol "TCP" - end - elsif params[:connection_protocol].casecmp("ssh").zero? - xml.InputEndpoint do - xml.LocalPort "22" - xml.Name "SSH" - xml.Port params[:port] - xml.Protocol "TCP" - end - end - all_endpoints = [] - - if params[:tcp_endpoints] - params[:tcp_endpoints].split(",").map(&:strip).each do |endpoint| - all_endpoints << parse_endpoint_from_params("TCP", params[:azure_vm_name], endpoint) - end - end - if params[:udp_endpoints] - params[:udp_endpoints].split(",").map(&:strip).each do |endpoint| - all_endpoints << parse_endpoint_from_params("UDP", params[:azure_vm_name], endpoint) - end - end - add_endpoints_to_xml(xml, all_endpoints, params) if all_endpoints.any? - end - if params[:azure_subnet_name] - xml.SubnetNames do - xml.SubnetName params[:azure_subnet_name] - end - end - end - end - - # Azure resource extension support - if params[:connection_protocol] == "cloud-api" - xml.ResourceExtensionReferences do - xml.ResourceExtensionReference do - xml.ReferenceName params[:chef_extension] - xml.Publisher params[:chef_extension_publisher] - xml.Name params[:chef_extension] - xml.Version params[:chef_extension_version] - xml.ResourceExtensionParameterValues do - if params[:chef_extension_public_param] - xml.ResourceExtensionParameterValue do - xml.Key "PublicParams" - xml.Value Base64.encode64(params[:chef_extension_public_param].to_json) - xml.Type "Public" - end - end - if params[:chef_extension_private_param] - xml.ResourceExtensionParameterValue do - xml.Key "PrivateParams" - xml.Value Base64.encode64(params[:chef_extension_private_param].to_json) - xml.Type "Private" - end - end - end - xml.State "Enable" - end - end - end - - if params[:azure_availability_set] - xml.AvailabilitySetName params[:azure_availability_set] - end - - xml.VMImageName params[:azure_source_image] if params[:is_vm_image] - - xml.Label Base64.encode64(params[:azure_vm_name]).strip - - # OSVirtualHardDisk not required in case azure_source_image is a VMImage - unless params[:is_vm_image] - xml.OSVirtualHardDisk do - disk_name = params[:azure_os_disk_name] || "disk_" + SecureRandom.uuid - xml.DiskName disk_name - domain_suffix = params[:azure_api_host_name] ? params[:azure_api_host_name].scan(/core.*/)[0] : "" - xml.MediaLink "http://" + params[:azure_storage_account] + ".blob." + domain_suffix + "/vhds/" + disk_name + ".vhd" - xml.SourceImageName params[:azure_source_image] - end - end - - xml.RoleSize params[:azure_vm_size] - xml.ProvisionGuestAgent true if params[:connection_protocol] == "cloud-api" - end - end - builder.doc - end - - def create(params, roleXML) - servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments" \ - "/#{params["deploy_name"]}/roles" - @connection.query_azure(servicecall, "post", roleXML.to_xml) - end - - def setup_extension(params) - ## add Chef Extension config in role_xml retrieved from the server - puts "Adding Chef Extension config in server role..." - role_xml = update_role_xml_for_extension(params[:role_xml], params) - - ## role_xml can't be used for update as it has additional tags like - ## role_name, osversion etc. which update API does not support, also the - ## xml is the child of parent node 'Deployment' in XML, so instead of - ## modifying the role_xml to fit for our requirements, we create - ## new XML (with Chef Extension config and other pre-existing VM config) - ## using the required values of the updated role_xml - builder = Nokogiri::XML::Builder.new do |xml| - xml.PersistentVMRole( - "xmlns" => "http://schemas.microsoft.com/windowsazure", - "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance" - ) do - xml.ConfigurationSets role_xml.at_css("ConfigurationSets").children unless role_xml.at_css("ConfigurationSets").nil? - xml.ResourceExtensionReferences role_xml.at_css("ResourceExtensionReferences").children unless role_xml.at_css("ResourceExtensionReferences").nil? - xml.AvailabilitySetName role_xml.at_css("AvailabilitySetName").children unless role_xml.at_css("AvailabilitySetName").nil? - xml.DataVirtualHardDisks role_xml.at_css("DataVirtualHardDisks").children unless role_xml.at_css("DataVirtualHardDisks").nil? - xml.OSVirtualHardDisk role_xml.at_css("OSVirtualHardDisk").children unless role_xml.at_css("OSVirtualHardDisk").nil? - xml.RoleSize role_xml.at_css("RoleSize").children unless role_xml.at_css("RoleSize").nil? - xml.ProvisionGuestAgent role_xml.at_css("ProvisionGuestAgent").children unless role_xml.at_css("ProvisionGuestAgent").nil? - end - end - - builder.doc.to_xml.gsub("<\;", "<").gsub(">\;", ">") - end - - def update_role_xml_for_extension(roleXML, params) - ## check if 'ResourceExtensionReferences' node already exist in the XML, - ## if no add it, else retrieve the object of the existing node - add_resource_extension_references = roleXML.at_css("ResourceExtensionReferences").nil? - - if add_resource_extension_references - resource_extension_references = Nokogiri::XML::Node.new("ResourceExtensionReferences", roleXML) - else - resource_extension_references = roleXML.css("ResourceExtensionReferences") - end - - ## check if Azure Chef Extension is already installed on the given server, - ## if no than install it, else raise error saying that the extension is - ## already installed - ext = nil - unless add_resource_extension_references - unless resource_extension_references.at_css("ReferenceName").nil? - resource_extension_references.css("ReferenceName").each { |node| ext = node if node.content == params[:chef_extension] } - end - end - - add_resource_extension_reference = ext.nil? - - ## create Azure Chef Extension config and add it in the role_xml - if add_resource_extension_reference - resource_extension_reference = Nokogiri::XML::Node.new("ResourceExtensionReference", roleXML) - - reference_name = Nokogiri::XML::Node.new("ReferenceName", roleXML) - reference_name.content = params[:chef_extension] - resource_extension_reference.add_child(reference_name) - - publisher = Nokogiri::XML::Node.new("Publisher", roleXML) - publisher.content = params[:chef_extension_publisher] - resource_extension_reference.add_child(publisher) - - name = Nokogiri::XML::Node.new("Name", roleXML) - name.content = params[:chef_extension] - resource_extension_reference.add_child(name) - - version = Nokogiri::XML::Node.new("Version", roleXML) - version.content = params[:chef_extension_version] - resource_extension_reference.add_child(version) - - resource_extension_parameter_values = Nokogiri::XML::Node.new("ResourceExtensionParameterValues", roleXML) - if params[:chef_extension_public_param] - resource_extension_parameter_value = Nokogiri::XML::Node.new("ResourceExtensionParameterValue", roleXML) - - key = Nokogiri::XML::Node.new("Key", roleXML) - key.content = "PublicParams" - resource_extension_parameter_value.add_child(key) - - value = Nokogiri::XML::Node.new("Value", roleXML) - value.content = Base64.encode64(params[:chef_extension_public_param].to_json) - resource_extension_parameter_value.add_child(value) - - type = Nokogiri::XML::Node.new("Type", roleXML) - type.content = "Public" - resource_extension_parameter_value.add_child(type) - - resource_extension_parameter_values.add_child(resource_extension_parameter_value) - end - - if params[:chef_extension_private_param] - resource_extension_parameter_value = Nokogiri::XML::Node.new("ResourceExtensionParameterValue", roleXML) - - key = Nokogiri::XML::Node.new("Key", roleXML) - key.content = "PrivateParams" - resource_extension_parameter_value.add_child(key) - - value = Nokogiri::XML::Node.new("Value", roleXML) - value.content = Base64.encode64(params[:chef_extension_private_param].to_json) - resource_extension_parameter_value.add_child(value) - - type = Nokogiri::XML::Node.new("Type", roleXML) - type.content = "Private" - resource_extension_parameter_value.add_child(type) - - resource_extension_parameter_values.add_child(resource_extension_parameter_value) - end - - resource_extension_reference.add_child(resource_extension_parameter_values) - - state = Nokogiri::XML::Node.new("State", roleXML) - state.content = "enable" - resource_extension_reference.add_child(state) - - if add_resource_extension_references - resource_extension_references.add_child(resource_extension_reference) - else - resource_extension_references.last.add_child(resource_extension_reference) - end - - roleXML.add_child(resource_extension_references) if add_resource_extension_references - - add_provision_guest_agent = roleXML.at_css("ProvisionGuestAgent").nil? - - if add_provision_guest_agent - provision_guest_agent = Nokogiri::XML::Node.new("ProvisionGuestAgent", roleXML) - provision_guest_agent.content = true - else - provision_guest_agent = roleXML.css("ProvisionGuestAgent") - provision_guest_agent.first.content = true - end - - roleXML.add_child(provision_guest_agent) if add_provision_guest_agent - else ## raise error as Chef Extension is already installed on the server - raise "Chef Extension is already installed on the server #{params[:azure_vm_name]}." - end - - roleXML - end - - def update(name, params, roleXML) - puts "Updating server role..." - servicecall = "hostedservices/#{params[:azure_dns_name]}" \ - "/deployments/#{params[:deploy_name]}/roles/#{name}" - ret_val = @connection.query_azure(servicecall, "put", roleXML, "", true, true, "application/xml") - error_code, error_message = error_from_response_xml(ret_val) - unless error_code.empty? - Chef::Log.debug(ret_val.to_s) - raise "Unable to update role:" + error_code + " : " + error_message - end - end - end -end diff --git a/lib/azure/service_management/storageaccount.rb b/lib/azure/service_management/storageaccount.rb deleted file mode 100644 index 8b947446..00000000 --- a/lib/azure/service_management/storageaccount.rb +++ /dev/null @@ -1,127 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class StorageAccounts - include AzureUtility - def initialize(connection) - @connection = connection - end - - # force_load should be true when there is something in local cache and we want to reload - # first call is always load. - def load(force_load = false) - unless @azure_storage_accounts || force_load - @azure_storage_accounts = begin - azure_storage_accounts = {} - responseXML = @connection.query_azure("storageservices") - servicesXML = responseXML.css("StorageServices StorageService") - servicesXML.each do |serviceXML| - storage = StorageAccount.new(@connection).parse(serviceXML) - azure_storage_accounts[storage.name] = storage - end - azure_storage_accounts - end - end - @azure_storage_accounts - end - - def all - load.values - end - - # first look up local cache if we have already loaded list. - def exists?(name) - return @azure_storage_accounts.key?(name) if @azure_storage_accounts - - exists_on_cloud?(name) - end - - # Look up on cloud and not local cache - def exists_on_cloud?(name) - ret_val = @connection.query_azure("storageservices/#{name}") - error_code, error_message = error_from_response_xml(ret_val) if ret_val - if ret_val.nil? || error_code.length > 0 - Chef::Log.warn "Unable to find storage account:" + error_message + " : " + error_message if ret_val - false - else - true - end - end - - def create(params) - storage = StorageAccount.new(@connection) - storage.create(params) - end - - def clear_unattached - all.each do |storage| - next unless storage.attached == false - - @connection.query_azure("storageaccounts/" + storage.name, "delete") - end - end - - def delete(name) - if exists?(name) - servicecall = "storageservices/" + name - @connection.query_azure(servicecall, "delete") - end - end - end -end - -module Azure - class StorageAccount - include AzureUtility - attr_accessor :name, :location - attr_accessor :affinityGroup, :location, :georeplicationenabled - def initialize(connection) - @connection = connection - end - - def parse(serviceXML) - @name = xml_content(serviceXML, "ServiceName") - @description = xml_content(serviceXML, "Description") - @label = xml_content(serviceXML, "Label") - @affinitygroup = xml_content(serviceXML, "AffinityGroup") - @location = xml_content(serviceXML, "Location") - @georeplicationenabled = xml_content(serviceXML, "GeoReplicationEnabled") - @extendpropertyname = xml_content(serviceXML, "ExtendedProperties ExtendedProperty Name") - @extendpropertyvalue = xml_content(serviceXML, "ExtendedProperties ExtendedProperty Value") - self - end - - def create(params) - builder = Nokogiri::XML::Builder.new do |xml| - xml.CreateStorageServiceInput("xmlns" => "http://schemas.microsoft.com/windowsazure") do - xml.ServiceName params[:azure_storage_account] - xml.Label Base64.encode64(params[:azure_storage_account]) - xml.Description params[:azure_storage_account_description] || "Explicitly created storage service" - # Location defaults to 'West US' - if params[:azure_affinity_group] - xml.AffinityGroup params[:azure_affinity_group] - else - xml.Location params[:azure_service_location] || "West US" - end - end - end - @connection.query_azure("storageservices", "post", builder.to_xml) - end - end -end diff --git a/lib/azure/service_management/utility.rb b/lib/azure/service_management/utility.rb deleted file mode 100755 index c29548d3..00000000 --- a/lib/azure/service_management/utility.rb +++ /dev/null @@ -1,40 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module AzureUtility - def xml_content(xml, key, default = "") - content = default - node = xml.at_css(key) - if node - content = node.content - end - content - end - - def error_from_response_xml(response_xml) - error_code_and_message = ["", ""] - error_node = response_xml.at_css("Error") - - if error_node - error_code_and_message[0] = xml_content(error_node, "Code") - error_code_and_message[1] = xml_content(error_node, "Message") - end - - error_code_and_message - end -end diff --git a/lib/azure/service_management/vnet.rb b/lib/azure/service_management/vnet.rb deleted file mode 100644 index 20fdb358..00000000 --- a/lib/azure/service_management/vnet.rb +++ /dev/null @@ -1,134 +0,0 @@ -# -# Author:: Jeff Mendoza (jeffmendoza@live.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -module Azure - class Vnets - def initialize(connection) - @connection = connection - end - - def load - @vnets ||= begin - @vnets = {} - response = @connection.query_azure("networking/virtualnetwork") - response.css("VirtualNetworkSite").each do |vnet| - item = Vnet.new(@connection).parse(vnet) - @vnets[item.name] = item - end - @vnets - end - end - - def all - load.values - end - - def exists?(name) - load.key?(name) - end - - def find(name) - load[name] - end - - def create(params) - ag = Vnet.new(@connection) - ag.create(params) - end - end -end - -module Azure - class Vnet - attr_accessor :name, :affinity_group, :state - - def initialize(connection) - @connection = connection - end - - def parse(image) - @name = image.at_css("Name").content - @affinity_group = image.at_css("AffinityGroup") ? image.at_css("AffinityGroup").content : "" - @state = image.at_css("State").content - self - end - - def create(params) - response = @connection.query_azure("networking/media") - if response.at_css("Error") && response.at_css("Code").text == "ResourceNotFound" - builder = Nokogiri::XML::Builder.new do |xml| - xml.NetworkConfiguration( - "xmlns" => "http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration" - ) do - - xml.VirtualNetworkConfiguration do - xml.VirtualNetworkSites do - xml.VirtualNetworkSite("name" => params[:azure_vnet_name], "AffinityGroup" => params[:azure_ag_name]) do - if params[:azure_address_space] - xml.AddressSpace do - xml.AddressPrefix params[:azure_address_space] - end - end - xml.Subnets do - xml.Subnet("name" => params[:azure_subnet_name]) do - xml.AddressPrefix params[:azure_address_space] - end - end - end - end - end - end - end - puts("Creating New Virtual Network: #{params[:azure_vnet_name]}...") - response = builder - else - vnets = response.css("VirtualNetworkSite") - vnet = nil - vnets.each { |vn| vnet = vn if vn["name"] == params[:azure_vnet_name] } - add = vnet.nil? - vnet = Nokogiri::XML::Node.new("VirtualNetworkSite", response) if add - vnet["name"] = params[:azure_vnet_name] - vnet["AffinityGroup"] = params[:azure_ag_name] - if add || !vnet.at_css("AddressSpace") ## create a new AddressSpace block in XML if VNet or AddressSpace block does not already exist - addr_space = Nokogiri::XML::Node.new("AddressSpace", response) - else ## retrieve object of existing AddressSpace if VNet or AddressSpace already exist - addr_space = vnet.at_css("AddressSpace") - end - addr_prefix = Nokogiri::XML::Node.new("AddressPrefix", response) - addr_prefix.content = params[:azure_address_space] - if add || !vnet.at_css("Subnets") ## create a new Subnets block in XML if VNet or Subnets block does not already exist - subnets = Nokogiri::XML::Node.new("Subnets", response) - else ## retrieve object of existing Subnets if VNet or Subnets already exist - subnets = vnet.at_css("Subnets") - end - saddr_prefix = Nokogiri::XML::Node.new("AddressPrefix", response) - saddr_prefix.content = params[:azure_address_space] - subnet = Nokogiri::XML::Node.new("Subnet", response) - subnet["name"] = params[:azure_subnet_name] - subnet.children = saddr_prefix - subnets.children = subnet - vnet.add_child(subnets) if add || !vnet.at_css("Subnets") - addr_space.children = addr_prefix - vnet.add_child(addr_space) if add || !vnet.at_css("AddressSpace") - vnets.last.add_next_sibling(vnet) if add - puts("Updating existing Virtual Network: #{params[:azure_vnet_name]}...") - end - @connection.query_azure("networking/media", "put", response.to_xml) - end - end -end diff --git a/lib/chef/knife/azure_ag_create.rb b/lib/chef/knife/azure_ag_create.rb deleted file mode 100644 index e66c58fc..00000000 --- a/lib/chef/knife/azure_ag_create.rb +++ /dev/null @@ -1,73 +0,0 @@ -# -# Author:: Jeff Mendoza (jeffmendoza@live.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureAgCreate < Knife - include Knife::AzureBase - - banner "knife azure ag create (options)" - - option :azure_affinity_group, - short: "-a GROUP", - long: "--azure-affinity-group GROUP", - description: "Specifies new affinity group name." - - option :azure_ag_desc, - long: "--azure-ag-desc DESC", - description: "Optional. Description for new affinity group." - - option :azure_service_location, - short: "-m LOCATION", - long: "--azure-service-location LOCATION", - description: "Specifies the geographic location - the name of "\ - "the data center location that is valid for your "\ - "subscription. Eg: West US, East US, "\ - "East Asia, Southeast Asia, North Europe, West Europe" - - def run - $stdout.sync = true - - Chef::Log.info("validating...") - validate_asm_keys!(:azure_affinity_group, - :azure_service_location) - - params = { - azure_ag_name: config[:azure_affinity_group], - azure_ag_desc: config[:azure_ag_desc], - azure_location: config[:azure_service_location], - } - - rsp = service.create_affinity_group(params) - print "\n" - if rsp.at_css("Status").nil? - if rsp.at_css("Code").nil? || rsp.at_css("Message").nil? - puts "Unknown Error. try -VV" - else - puts "#{rsp.at_css("Code").content}: "\ - "#{rsp.at_css("Message").content}" - end - else - puts "Creation status: #{rsp.at_css("Status").content}" - end - end - end - end -end diff --git a/lib/chef/knife/azure_ag_list.rb b/lib/chef/knife/azure_ag_list.rb deleted file mode 100644 index 645b4d89..00000000 --- a/lib/chef/knife/azure_ag_list.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Author:: Jeff Mendoza (jeffmendoza@live.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureAgList < Knife - include Knife::AzureBase - - banner "knife azure ag list (options)" - - def run - $stdout.sync = true - validate_asm_keys! - service.list_affinity_groups - end - end - end -end diff --git a/lib/chef/knife/azure_image_list.rb b/lib/chef/knife/azure_image_list.rb deleted file mode 100755 index 6bdc71c3..00000000 --- a/lib/chef/knife/azure_image_list.rb +++ /dev/null @@ -1,56 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Author:: Seth Chisamore () -# Author:: Adam Jacob () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureImageList < Knife - - include Knife::AzureBase - - banner "knife azure image list (options)" - - option :show_all_fields, - long: "--full", - default: false, - boolean: true, - description: "Show all the fields of the images" - - def run - $stdout.sync = true - - validate_asm_keys! - items = service.list_images - - image_labels = !config[:show_all_fields] ? %w{Name OS Location} : %w{Name Category Label OS Location} - image_list = image_labels.map { |label| ui.color(label, :bold) } - - image_items = image_labels.map(&:downcase) - items.each do |image| - image_items.each { |item| image_list << image.send(item).to_s } - end - - puts "\n" - puts ui.list(image_list, :uneven_columns_across, !config[:show_all_fields] ? 3 : 5) - end - end - end -end diff --git a/lib/chef/knife/azure_internal-lb_create.rb b/lib/chef/knife/azure_internal-lb_create.rb deleted file mode 100644 index 7b96b530..00000000 --- a/lib/chef/knife/azure_internal-lb_create.rb +++ /dev/null @@ -1,74 +0,0 @@ -# -# Author:: Aiman Alsari (aiman.alsari@gmail.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureInternalLbCreate < Knife - include Knife::AzureBase - - banner "knife azure internal lb create (options)" - - option :azure_load_balancer, - short: "-n NAME", - long: "--azure-load-balancer NAME", - description: "Required. Specifies new load balancer name." - - option :azure_lb_static_vip, - long: "--azure-lb-static-vip VIP", - description: "Optional. The Virtual IP that will be used for the load balancer." - - option :azure_subnet_name, - long: "--azure-subnet-name SUBNET_NAME", - description: "Required if static VIP is set. Specifies the subnet name "\ - "the load balancer is located in." - - option :azure_dns_name, - long: "--azure-dns-name DNS_NAME", - description: "The DNS prefix name that will be used to add this load balancer to. This must be an existing service/deployment." - - def run - $stdout.sync = true - - Chef::Log.info("validating...") - validate_asm_keys!(:azure_load_balancer) - - params = { - azure_load_balancer: config[:azure_load_balancer], - azure_lb_static_vip: config[:azure_lb_static_vip], - azure_subnet_name: config[:azure_subnet_name], - azure_dns_name: config[:azure_dns_name], - } - - rsp = service.create_internal_lb(params) - print "\n" - if rsp.at_css("Status").nil? - if rsp.at_css("Code").nil? || rsp.at_css("Message").nil? - puts "Unknown Error. try -VV" - else - puts "#{rsp.at_css("Code").content}: "\ - "#{rsp.at_css("Message").content}" - end - else - puts "Creation status: #{rsp.at_css("Status").content}" - end - end - end - end -end diff --git a/lib/chef/knife/azure_internal-lb_list.rb b/lib/chef/knife/azure_internal-lb_list.rb deleted file mode 100644 index 983bf020..00000000 --- a/lib/chef/knife/azure_internal-lb_list.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Author:: Aiman Alsari (aiman.alsari@gmail.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureInternalLbList < Knife - include Knife::AzureBase - - banner "knife azure internal lb list (options)" - - def run - $stdout.sync = true - validate_asm_keys! - service.list_internal_lb - end - end - end -end diff --git a/lib/chef/knife/azure_server_create.rb b/lib/chef/knife/azure_server_create.rb deleted file mode 100644 index 8e7b557e..00000000 --- a/lib/chef/knife/azure_server_create.rb +++ /dev/null @@ -1,531 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Author:: Adam Jacob () -# Author:: Seth Chisamore () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" -require "chef/knife/bootstrap" -require "chef/knife/bootstrap/client_builder" -require_relative "bootstrap/common_bootstrap_options" -require_relative "bootstrap/bootstrapper" - -class Chef - class Knife - class AzureServerCreate < Knife::Bootstrap - include Knife::AzureBase - include Knife::Bootstrap::CommonBootstrapOptions - include Knife::Bootstrap::Bootstrapper - - deps do - require "securerandom" unless defined?(SecureRandom) - require "readline" - require "chef/json_compat" - require "chef/knife/bootstrap" - require "chef/knife/core/windows_bootstrap_context" - Chef::Knife::Bootstrap.load_deps - end - - banner "knife azure server create (options)" - - SUPPORTED_CONNECTION_PROTOCOLS = %w{ssh winrm cloud-api}.freeze - - attr_accessor :initial_sleep_delay - - option :azure_affinity_group, - short: "-a GROUP", - long: "--azure-affinity-group GROUP", - description: "Required if not using a Service Location. Specifies Affinity Group the VM should belong to." - - option :azure_dns_name, - short: "-d DNS_NAME", - long: "--azure-dns-name DNS_NAME", - description: "The DNS prefix name that can be used to access the cloud service which is unique within Windows Azure. Default is 'azure-dns-any_random_text'(e.g: azure-dns-be9b0f6f-7dda-456f-b2bf-4e28a3bc0add). - If you want to add new VM to an existing service/deployment, specify an exiting dns-name, - along with --azure-connect-to-existing-dns option. - Otherwise a new deployment is created. For example, if the DNS of cloud service is MyService you could access the cloud service - by calling: http://DNS_NAME.cloudapp.net" - - option :azure_source_image, - short: "-I IMAGE", - long: "--azure-source-image IMAGE", - description: "Required. Specifies the name of the disk image to use to create the virtual machine. - Do a \"knife azure image list\" to see a list of available images." - - option :udp_endpoints, - short: "-u PORT_LIST", - long: "--udp-endpoints PORT_LIST", - description: "Comma-separated list of UDP local and public ports to open e.g. '80:80,433:5000'" - - option :azure_connect_to_existing_dns, - short: "-c", - long: "--azure-connect-to-existing-dns", - boolean: true, - default: false, - description: "Set this flag to add the new VM to an existing deployment/service. Must give the name of the existing - DNS correctly in the --dns-name option" - - option :azure_network_name, - long: "--azure-network-name NETWORK_NAME", - description: "Optional. Specifies the network of virtual machine" - - option :azure_subnet_name, - long: "--azure-subnet-name SUBNET_NAME", - description: "Optional. Specifies the subnet of virtual machine" - - option :azure_vm_startup_timeout, - long: "--azure-vm-startup-timeout TIMEOUT", - description: "The number of minutes that knife-azure will wait for the virtual machine to reach the 'provisioning' state. Default is 10.", - default: 10 - - option :azure_vm_ready_timeout, - long: "--azure-vm-ready-timeout TIMEOUT", - description: "The number of minutes that knife-azure will wait for the virtual machine state to transition from 'provisioning' to 'ready'. Default is 15.", - default: 15 - - option :auth_timeout, - long: "--windows-auth-timeout MINUTES", - description: "The maximum time in minutes to wait to for authentication over the transport to the node to succeed. The default value is 25 minutes.", - default: 25 - - option :identity_file_passphrase, - long: "--identity-file-passphrase PASSWORD", - description: "SSH key passphrase. Optional, specify if passphrase for identity-file exists" - - option :winrm_max_timeout, - long: "--winrm-max-timeout MINUTES", - description: "Set winrm maximum command timeout in minutes, useful for long bootstraps" - - option :winrm_max_memory_per_shell, - long: "--winrm-max-memory-per-shell", - description: "Set winrm max memory per shell in MB" - - option :azure_domain_name, - long: "--azure-domain-name DOMAIN_NAME", - description: 'Optional. Specifies the domain name to join. If the domains name is not specified, --azure-domain-user must specify the user principal name (UPN) format (user@fully-qualified-DNS-domain) or the fully-qualified-DNS-domain\\username format' - - option :azure_domain_ou_dn, - long: "--azure-domain-ou-dn DOMAIN_OU_DN", - description: "Optional. Specifies the (LDAP) X 500-distinguished name of the organizational unit (OU) in which the computer account is created. This account is in Active Directory on a domain controller in the domain to which the computer is being joined. Example: OU=HR,dc=opscode,dc=com" - - option :azure_domain_user, - long: "--azure-domain-user DOMAIN_USER_NAME", - description: 'Optional. Specifies the username who has access to join the domain. - Supported format: username(if domain is already specified in --azure-domain-name option), - fully-qualified-DNS-domain\username, user@fully-qualified-DNS-domain' - - option :azure_domain_passwd, - long: "--azure-domain-passwd DOMAIN_PASSWD", - description: "Optional. Specifies the password for domain user who has access to join the domain." - - # Overriding this option to provide "cloud-api" in SUPPORTED_CONNECTION_PROTOCOLS - option :connection_protocol, - short: "-o PROTOCOL", - long: "--connection-protocol PROTOCOL", - description: "The protocol to use to connect to the target node.", - in: SUPPORTED_CONNECTION_PROTOCOLS - - # run() would be executing from parent class - # Chef::Knife::Bootstrap, defined in core. - # Required methods have been overridden here - #### run() execution begins #### - - def plugin_setup!; end - - def validate_name_args!; end - - # Ensure a valid protocol is provided for target host connection - # - # The method call will cause the program to exit(1) if: - # * Conflicting protocols are given via the target URI and the --protocol option - # * The protocol is not a supported protocol - # - # @note we are overriding this method here to consider "cloud-api" as valid protocol - # - # @return [TrueClass] If options are valid. - def validate_protocol! - from_cli = config[:connection_protocol] - if from_cli && connection_protocol != from_cli - # Hanging indent to align with the ERROR: prefix - ui.error <<~EOM - The URL '#{host_descriptor}' indicates protocol is '#{connection_protocol}' - while the --protocol flag specifies '#{from_cli}'. Please include - only one or the other. - EOM - exit 1 - end - - unless SUPPORTED_CONNECTION_PROTOCOLS.include?(connection_protocol) - ui.error <<~EOM - Unsupported protocol '#{connection_protocol}'. - - Supported protocols are: #{SUPPORTED_CONNECTION_PROTOCOLS.join(" ")} - EOM - exit 1 - end - true - end - - def plugin_validate_options! - Chef::Log.info("Validating...") - validate_asm_keys!(:azure_source_image) - validate_params! - end - - def plugin_create_instance! - Chef::Log.info("Creating...") - set_defaults - server_def = create_server_def - vm_details = service.create_server(server_def) - - wait_until_virtual_machine_ready - - config[:connection_port] = server_def[:port] - config[:connection_protocol] = server_def[:connection_protocol] - config[:chef_node_name] = config[:chef_node_name] || server_name - rescue => error - ui.error("Something went wrong. Please use -VV option for more details.") - Chef::Log.debug(error.backtrace.join("\n").to_s) - exit 1 - end - - def server_name - @server_name ||= if @server.nil? - nil - elsif !@server.hostedservicename.nil? - @server.hostedservicename + ".cloudapp.net" - else - @server.ipaddress - end - end - - alias host_descriptor server_name - - def plugin_finalize - if config[:connection_protocol] == "cloud-api" && config[:extended_logs] - print "\nWaiting for the first chef-client run" - fetch_chef_client_logs(Time.now, 30) - end - msg_server_summary(@server) - end - - #### run() execution ends #### - - def wait_until_virtual_machine_ready(retry_interval_in_seconds = 30) - vm_status = nil - begin - azure_vm_startup_timeout = config[:azure_vm_startup_timeout].to_i - azure_vm_ready_timeout = config[:azure_vm_ready_timeout].to_i - vm_status = wait_for_virtual_machine_state(:vm_status_provisioning, azure_vm_startup_timeout, retry_interval_in_seconds) - if vm_status != :vm_status_ready - begin - wait_for_virtual_machine_state(:vm_status_ready, azure_vm_ready_timeout, retry_interval_in_seconds) - rescue Chef::Exceptions::CommandTimeout => e - ui.warn("\n#{e.message}") - ui.warn("Ignoring failure to reach 'ready' with bootstrap.") - end - end - - msg_server_summary(@server) - - if config[:connection_protocol] == "cloud-api" - extension_status = wait_for_resource_extension_state(:wagent_provisioning, 5, retry_interval_in_seconds) - - if extension_status != :extension_installing - extension_status = wait_for_resource_extension_state(:extension_installing, 5, retry_interval_in_seconds) - end - - if extension_status != :extension_provisioning - extension_status = wait_for_resource_extension_state(:extension_provisioning, 10, retry_interval_in_seconds) - end - - if extension_status != :extension_ready - wait_for_resource_extension_state(:extension_ready, 5, retry_interval_in_seconds) - end - end - rescue Exception => e - Chef::Log.error("#{e}") - raise "Verify connectivity to Azure and subscription resource limit compliance (e.g. maximum CPU core limits) and try again." - end - end - - def wait_for_virtual_machine_state(vm_status_goal, total_wait_time_in_minutes, retry_interval_in_seconds) - vm_status_ordering = { vm_status_not_detected: 0, vm_status_provisioning: 1, vm_status_ready: 2 } - vm_status_description = { vm_status_not_detected: "any", vm_status_provisioning: "provisioning", vm_status_ready: "ready" } - - print ui.color("\nWaiting for virtual machine to reach status '#{vm_status_description[vm_status_goal]}'\n", :magenta) - - total_wait_time_in_seconds = total_wait_time_in_minutes * 60 - max_polling_attempts = total_wait_time_in_seconds / retry_interval_in_seconds - polling_attempts = 0 - - wait_start_time = Time.now - - begin - vm_status = get_virtual_machine_status - vm_ready = vm_status_ordering[vm_status] >= vm_status_ordering[vm_status_goal] - print "." - sleep retry_interval_in_seconds unless vm_ready - polling_attempts += 1 - end until vm_ready || polling_attempts >= max_polling_attempts - - unless vm_ready - raise Chef::Exceptions::CommandTimeout, "Virtual machine state '#{vm_status_description[vm_status_goal]}' not reached after #{total_wait_time_in_minutes} minutes." - end - - elapsed_time_in_minutes = ((Time.now - wait_start_time) / 60).round(2) - print ui.color("\nvm state '#{vm_status_description[vm_status_goal]}' reached after #{elapsed_time_in_minutes} minutes.\n", :cyan) - vm_status - end - - def wait_for_resource_extension_state(extension_status_goal, total_wait_time_in_minutes, retry_interval_in_seconds) - extension_status_ordering = { extension_status_not_detected: 0, wagent_provisioning: 1, extension_installing: 2, extension_provisioning: 3, extension_ready: 4 } - - status_description = { extension_status_not_detected: "any", wagent_provisioning: "wagent provisioning", extension_installing: "installing", extension_provisioning: "provisioning", extension_ready: "ready" } - - print ui.color("\nWaiting for Resource Extension to reach status '#{status_description[extension_status_goal]}'\n", :magenta) - - max_polling_attempts = (total_wait_time_in_minutes * 60) / retry_interval_in_seconds - polling_attempts = 0 - - wait_start_time = Time.now - - begin - extension_status = get_extension_status - extension_ready = extension_status_ordering[extension_status[:status]] >= extension_status_ordering[extension_status_goal] - print "." - sleep retry_interval_in_seconds unless extension_ready - polling_attempts += 1 - end until extension_ready || polling_attempts >= max_polling_attempts - - unless extension_ready - raise Chef::Exceptions::CommandTimeout, "Resource extension state '#{status_description[extension_status_goal]}' not reached after #{total_wait_time_in_minutes} minutes. #{extension_status[:message]}" - end - - elapsed_time_in_minutes = ((Time.now - wait_start_time) / 60).round(2) - print ui.color("\nResource extension state '#{status_description[extension_status_goal]}' reached after #{elapsed_time_in_minutes} minutes.\n", :cyan) - - extension_status[:status] - end - - def get_virtual_machine_status - @server = service.get_role_server(config[:azure_dns_name], config[:azure_vm_name]) - if @server.nil? - :vm_status_not_detected - else - Chef::Log.debug("Role status is #{@server.status}") - case @server.status.to_s - when "ReadyRole" - :vm_status_ready - when "Provisioning" - :vm_status_provisioning - else - :vm_status_not_detected - end - end - end - - def get_extension_status - deployment_name = service.deployment_name(config[:azure_dns_name]) - deployment = service.deployment("hostedservices/#{config[:azure_dns_name]}/deployments/#{deployment_name}") - extension_status = {} - - if deployment.at_css("Deployment Name") != nil - role_list_xml = deployment.css("RoleInstanceList RoleInstance") - role_list_xml.each do |role| - if role.at_css("RoleName").text == config[:azure_vm_name] - lnx_waagent_fail_msg = "Failed to deserialize the status reported by the Guest Agent" - waagent_status_msg = role.at_css("GuestAgentStatus FormattedMessage Message").text - if role.at_css("GuestAgentStatus Status").text == "Ready" - extn_status = role.at_css("ResourceExtensionStatusList Status").text - Chef::Log.debug("Resource extension status is #{extn_status}") - if extn_status == "Installing" - extension_status[:status] = :extension_installing - extension_status[:message] = role.at_css("ResourceExtensionStatusList FormattedMessage Message").text - elsif extn_status == "NotReady" - extension_status[:status] = :extension_provisioning - extension_status[:message] = role.at_css("ResourceExtensionStatusList FormattedMessage Message").text - elsif extn_status == "Ready" - extension_status[:status] = :extension_ready - extension_status[:message] = role.at_css("ResourceExtensionStatusList FormattedMessage Message").text - else - extension_status[:status] = :extension_status_not_detected - end - # This fix is for linux waagent issue: api unable to deserialize the waagent status. - elsif (role.at_css("GuestAgentStatus Status").text == "NotReady") && (waagent_status_msg == lnx_waagent_fail_msg) - extension_status[:status] = :extension_ready - else - extension_status[:status] = :wagent_provisioning - extension_status[:message] = role.at_css("GuestAgentStatus Message").text - end - else - extension_status[:status] = :extension_status_not_detected - end - end - else - extension_status[:status] = :extension_status_not_detected - end - extension_status - end - - def create_server_def - server_def = { - azure_storage_account: config[:azure_storage_account], - azure_api_host_name: config[:azure_api_host_name], - azure_dns_name: config[:azure_dns_name], - azure_vm_name: config[:azure_vm_name], - azure_service_location: config[:azure_service_location], - azure_os_disk_name: config[:azure_os_disk_name], - azure_source_image: config[:azure_source_image], - azure_vm_size: config[:azure_vm_size], - tcp_endpoints: config[:tcp_endpoints], - udp_endpoints: config[:udp_endpoints], - connection_protocol: config[:connection_protocol], - azure_connect_to_existing_dns: config[:azure_connect_to_existing_dns], - connection_user: config[:connection_user], - azure_availability_set: config[:azure_availability_set], - azure_affinity_group: config[:azure_affinity_group], - azure_network_name: config[:azure_network_name], - azure_subnet_name: config[:azure_subnet_name], - ssl_cert_fingerprint: config[:thumbprint], - cert_path: config[:cert_path], - cert_password: config[:cert_passphrase], - winrm_ssl: config[:winrm_ssl], - winrm_max_timeout: config[:winrm_max_timeout].to_i * 60 * 1000, # converting minutes to milliseconds - winrm_max_memory_per_shell: config[:winrm_max_memory_per_shell], - } - - if config[:connection_protocol] == "cloud-api" - server_def[:chef_extension] = get_chef_extension_name - server_def[:chef_extension_publisher] = get_chef_extension_publisher - server_def[:chef_extension_version] = get_chef_extension_version - server_def[:chef_extension_public_param] = get_chef_extension_public_params - server_def[:chef_extension_private_param] = get_chef_extension_private_params - else - if is_image_windows? - # We can specify the AdminUsername after API version 2013-03-01. However, in this API version, - # the AdminUsername is a required parameter. - # Also, the user name cannot be Administrator, Admin, Admin1 etc, for enhanced security (provided by Azure) - if config[:connection_user].nil? || config[:connection_user].downcase =~ /admin*/ - ui.error("Connection User is compulsory parameter and it cannot be named 'admin*'") - exit 1 - # take cares of when user name contains domain - # azure add role api doesn't support '\\' in user name - elsif config[:connection_user].split('\\').length.eql?(2) - server_def[:connection_user] = config[:connection_user].split('\\')[1] - end - else - unless config[:connection_user] - ui.error("Connection User is compulsory parameter") - exit 1 - end - unless config[:connection_password] || config[:ssh_identity_file] - ui.error("Specify either SSH Key or SSH Password") - exit 1 - end - end - end - - if is_image_windows? - server_def[:os_type] = "Windows" - server_def[:admin_password] = config[:connection_password] - server_def[:connection_protocol] = config[:connection_protocol] || "winrm" - else - server_def[:os_type] = "Linux" - server_def[:connection_protocol] = config[:connection_protocol].nil? || config[:connection_protocol] == "winrm" ? "ssh" : config[:connection_protocol] - server_def[:connection_user] = config[:connection_user] - server_def[:connection_password] = config[:connection_password] - server_def[:ssh_identity_file] = config[:ssh_identity_file] - server_def[:identity_file_passphrase] = config[:identity_file_passphrase] - end - - azure_connect_to_existing_dns = config[:azure_connect_to_existing_dns] - if is_image_windows? && server_def[:connection_protocol] == "winrm" - port = config[:connection_port] || "5985" - port = config[:connection_port] || Random.rand(64000) + 1000 if azure_connect_to_existing_dns - elsif server_def[:connection_protocol] == "ssh" - port = config[:connection_port] || "22" - port = config[:connection_port] || Random.rand(64000) + 1000 if azure_connect_to_existing_dns - end - - server_def[:port] = port - - server_def[:is_vm_image] = service.vm_image?(config[:azure_source_image]) - server_def[:azure_domain_name] = config[:azure_domain_name] if config[:azure_domain_name] - - if config[:azure_domain_user] - # extract domain name since it should be part of username - case config[:azure_domain_user] - when /(\S+)\\(.+)/ # format - fully-qualified-DNS-domain\username - server_def[:azure_domain_name] = $1 if config[:azure_domain_name].nil? - server_def[:azure_user_domain_name] = $1 - server_def[:azure_domain_user] = $2 - when /(.+)@(\S+)/ # format - user@fully-qualified-DNS-domain - server_def[:azure_domain_name] = $2 if config[:azure_domain_name].nil? - server_def[:azure_user_domain_name] = $2 - server_def[:azure_domain_user] = $1 - else - if config[:azure_domain_name].nil? - ui.error('--azure-domain-name should be specified if --azure-domain-user is not in one of the following formats: fully-qualified-DNS-domain\username, user@fully-qualified-DNS-domain') - exit 1 - end - server_def[:azure_domain_user] = config[:azure_domain_user] - end - end - server_def[:azure_domain_passwd] = config[:azure_domain_passwd] - server_def[:azure_domain_ou_dn] = config[:azure_domain_ou_dn] - - server_def - end - - private - - def set_defaults - set_configs - end - - def set_configs - unless config[:connection_user].nil? - config[:connection_user] = config[:connection_user] - end - - unless config[:connection_password].nil? - config[:connection_password] = config[:connection_password] - end - - config[:azure_dns_name] = get_dns_name(config[:azure_dns_name]) - config[:azure_vm_name] = config[:azure_dns_name] unless config[:azure_vm_name] - config[:chef_node_name] = config[:azure_vm_name] unless config[:chef_node_name] - end - - # This is related to Windows VM's specifically and computer name - # length limits for legacy computer accounts - MAX_VM_NAME_CHARACTERS = 15 - - # generate a random dns_name if azure_dns_name is empty - def get_dns_name(azure_dns_name, prefix = "az-") - return azure_dns_name unless azure_dns_name.nil? - - if config[:azure_vm_name].nil? - (prefix + SecureRandom.hex((MAX_VM_NAME_CHARACTERS - prefix.length) / 2)) - else - config[:azure_vm_name] - end - end - end - end -end diff --git a/lib/chef/knife/azure_server_delete.rb b/lib/chef/knife/azure_server_delete.rb deleted file mode 100755 index 9e99f05d..00000000 --- a/lib/chef/knife/azure_server_delete.rb +++ /dev/null @@ -1,136 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Author:: Adam Jacob () -# Author:: Seth Chisamore () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureServerDelete < Knife - - include Knife::AzureBase - - deps do - # These two are needed for the '--purge' deletion case - require "chef/node" - require "chef/api_client" - end - - banner "knife azure server delete SERVER [SERVER] (options)" - - option :preserve_azure_os_disk, - long: "--preserve-azure-os-disk", - boolean: true, - default: false, - description: "Preserve corresponding OS Disk" - - option :preserve_azure_vhd, - long: "--preserve-azure-vhd", - boolean: true, - default: false, - description: "Preserve underlying VHD" - - option :purge, - short: "-P", - long: "--purge", - boolean: true, - default: false, - description: "Destroy corresponding node and client on the Chef Server, in addition to destroying the Windows Azure node itself. Assumes node and client have the same name as the server (if not, add the '--node-name' option)." - - option :chef_node_name, - short: "-N NAME", - 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_azure_dns_name, - long: "--preserve-azure-dns-name", - boolean: true, - default: false, - description: "Preserve corresponding cloud service (DNS). If the option is not set, it deletes the service not used by any VMs." - - option :delete_azure_storage_account, - long: "--delete-azure-storage-account", - boolean: true, - default: false, - description: "Delete corresponding storage account. If the option is set, it deletes the storage account not used by any VMs." - - option :azure_dns_name, - long: "--azure-dns-name NAME", - description: "specifies the DNS name (also known as hosted service name)" - - option :wait, - long: "--wait", - boolean: true, - default: false, - description: "Wait for server deletion. Default is false" - - # 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!) - # the user is already making their intent known. It is not - # necessary to make them confirm two more times. - def destroy_item(klass, name, type_name) - object = klass.load(name) - object.destroy - ui.warn("Deleted #{type_name} #{name}") - rescue Net::HTTPServerException - ui.warn("Could not find a #{type_name} named #{name} to delete. Please provide --node-name option and it's value") - end - - def validate_disk_and_storage - if config[:preserve_azure_os_disk] && config[:delete_azure_storage_account] - ui.warn("Cannot delete storage account while keeping OS Disk. Please set any one option.") - exit - else - true - end - end - - def run - validate_asm_keys! - validate_disk_and_storage - @name_args.each do |name| - begin - service.delete_server( { name: name, preserve_azure_os_disk: config[:preserve_azure_os_disk], - preserve_azure_vhd: config[:preserve_azure_vhd], - preserve_azure_dns_name: config[:preserve_azure_dns_name], - delete_azure_storage_account: config[:delete_azure_storage_account], - wait: config[:wait] } ) - - if config[:purge] - node_to_delete = config[:chef_node_name] || name - if node_to_delete - destroy_item(Chef::Node, node_to_delete, "node") - destroy_item(Chef::ApiClient, node_to_delete, "client") - else - ui.warn("Node name to purge not provided. Corresponding client node will remain on Chef Server.") - end - else - ui.warn("Corresponding node and client for the #{name} server were not deleted and remain registered with the Chef Server") - end - - rescue Exception => ex - ui.error("#{ex.message}") - ui.error("#{ex.backtrace.join("\n")}") - end - end - end - end - end -end diff --git a/lib/chef/knife/azure_server_list.rb b/lib/chef/knife/azure_server_list.rb deleted file mode 100755 index 945d5d98..00000000 --- a/lib/chef/knife/azure_server_list.rb +++ /dev/null @@ -1,38 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Author:: Seth Chisamore () -# Author:: Adam Jacob () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureServerList < Knife - - include Knife::AzureBase - - banner "knife azure server list (options)" - - def run - $stdout.sync = true - validate_asm_keys! - service.list_servers - end - end - end -end diff --git a/lib/chef/knife/azure_server_show.rb b/lib/chef/knife/azure_server_show.rb deleted file mode 100755 index 26f66d1f..00000000 --- a/lib/chef/knife/azure_server_show.rb +++ /dev/null @@ -1,41 +0,0 @@ -# -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Author:: Seth Chisamore () -# Author:: Adam Jacob () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureServerShow < Knife - - include Knife::AzureBase - - banner "knife azure server show SERVER [SERVER]" - - def run - $stdout.sync = true - validate_asm_keys! - @name_args.each do |name| - service.show_server name - end - end - - end - end -end diff --git a/lib/chef/knife/azure_vnet_create.rb b/lib/chef/knife/azure_vnet_create.rb deleted file mode 100644 index 4d847f14..00000000 --- a/lib/chef/knife/azure_vnet_create.rb +++ /dev/null @@ -1,74 +0,0 @@ -# -# Author:: Jeff Mendoza (jeffmendoza@live.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureVnetCreate < Knife - include Knife::AzureBase - - banner "knife azure vnet create (options)" - - option :azure_network_name, - short: "-n NETWORK_NAME", - long: "--azure-network-name NETWORK_NAME", - description: "Specifies the name of the virtual network to create." - - option :azure_affinity_group, - short: "-a GROUP", - long: "--azure-affinity-group GROUP", - description: "Specifies the affinity group to associate with the vnet." - - option :azure_address_space, - long: "--azure-address-space CIDR", - description: "Specifies the address space of the vnet using CIDR notation." - - option :azure_subnet_name, - long: "--azure-subnet-name CIDR", - description: "Specifies the Subnet Name." - - def run - $stdout.sync = true - - Chef::Log.info("validating...") - validate_asm_keys!(:azure_network_name, :azure_affinity_group, :azure_address_space) - - params = { - azure_vnet_name: config[:azure_network_name], - azure_ag_name: config[:azure_affinity_group], - azure_address_space: config[:azure_address_space], - azure_subnet_name: config[:azure_subnet_name] || "Subnet-#{Random.rand(10)}", - } - - rsp = service.create_vnet(params) - print "\n" - if rsp.at_css("Status").nil? - if rsp.at_css("Code").nil? || rsp.at_css("Message").nil? - puts "Unknown Error. try -VV" - else - puts "#{rsp.at_css("Code").content}: "\ - "#{rsp.at_css("Message").content}" - end - else - puts "Creation status: #{rsp.at_css("Status").content}" - end - end - end - end -end diff --git a/lib/chef/knife/azure_vnet_list.rb b/lib/chef/knife/azure_vnet_list.rb deleted file mode 100644 index e325cf94..00000000 --- a/lib/chef/knife/azure_vnet_list.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Author:: Jeff Mendoza (jeffmendoza@live.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" - -class Chef - class Knife - class AzureVnetList < Knife - include Knife::AzureBase - - banner "knife azure vnet list (options)" - - def run - $stdout.sync = true - validate_asm_keys! - service.list_vnets - end - end - end -end diff --git a/lib/chef/knife/azurerm_server_create.rb b/lib/chef/knife/azurerm_server_create.rb index b41ec117..7c9754f9 100644 --- a/lib/chef/knife/azurerm_server_create.rb +++ b/lib/chef/knife/azurerm_server_create.rb @@ -292,7 +292,7 @@ def set_default_image_reference! def set_os_image(publisher, img_offer, default_os_version) config[:azure_image_reference_publisher] = publisher config[:azure_image_reference_offer] = img_offer - config[:azure_image_reference_sku] = config[:azure_image_reference_sku] ? config[:azure_image_reference_sku] : default_os_version + config[:azure_image_reference_sku] = config[:azure_image_reference_sku] || default_os_version end def is_image_os_type? diff --git a/lib/chef/knife/bootstrap/bootstrapper.rb b/lib/chef/knife/bootstrap/bootstrapper.rb index 7592fff6..887874e3 100644 --- a/lib/chef/knife/bootstrap/bootstrapper.rb +++ b/lib/chef/knife/bootstrap/bootstrapper.rb @@ -49,16 +49,11 @@ def get_chef_extension_version(chef_extension_name = nil) config[:azure_chef_extension_version] else chef_extension_name ||= get_chef_extension_name - if @service.instance_of? Azure::ResourceManagement::ARMInterface - service.get_latest_chef_extension_version( - azure_service_location: config[:azure_service_location], - chef_extension_publisher: get_chef_extension_publisher, - chef_extension: chef_extension_name - ) - elsif @service.instance_of? Azure::ServiceManagement::ASMInterface - extensions = service.get_extension(chef_extension_name, get_chef_extension_publisher) - extensions.css("Version").max.text.split(".").first + ".*" - end + service.get_latest_chef_extension_version( + azure_service_location: config[:azure_service_location], + chef_extension_publisher: get_chef_extension_publisher, + chef_extension: chef_extension_name + ) end end diff --git a/lib/chef/knife/bootstrap_azure.rb b/lib/chef/knife/bootstrap_azure.rb deleted file mode 100644 index e1503d77..00000000 --- a/lib/chef/knife/bootstrap_azure.rb +++ /dev/null @@ -1,191 +0,0 @@ -# -# Author:: Aliasgar Batterywala (aliasgar.batterywala@clogeny.com) -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "helpers/azure_base" -require "chef/knife/bootstrap" -require_relative "bootstrap/common_bootstrap_options" -require_relative "bootstrap/bootstrapper" - -class Chef - class Knife - class BootstrapAzure < Knife::Bootstrap - include Knife::AzureBase - include Knife::Bootstrap::CommonBootstrapOptions - include Knife::Bootstrap::Bootstrapper - - banner "knife bootstrap azure SERVER (options)" - - option :azure_dns_name, - short: "-d DNS_NAME", - long: "--azure-dns-name DNS_NAME", - description: "Optional. The DNS prefix name that is used to access the cloud service." - - # run() would be executing from parent class - # Chef::Knife::Bootstrap, defined in core. - # Required methods have been overridden here - #### run() execution begins #### - - def plugin_setup!; end - - def validate_name_args!; end - - def plugin_validate_options! - ui.info "Validating..." - validate_asm_keys! - end - - def plugin_create_instance! - if @name_args.length == 1 - service.add_extension(@name_args[0], set_ext_params) - if config[:extended_logs] - print "\n\nWaiting for the Chef Extension to become available/ready" - wait_until_extension_available(Time.now, 10) - print "\n\nWaiting for the first chef-client run" - fetch_chef_client_logs(Time.now, 35) - end - else - raise ArgumentError, "Please specify the SERVER name which needs to be bootstrapped via the Chef Extension." if @name_args.empty? - raise ArgumentError, "Please specify only one SERVER name which needs to be bootstrapped via the Chef Extension." if @name_args.length > 1 - end - rescue StandardError => error - ui.error(error.message.to_s) - Chef::Log.debug(error.backtrace.join("\n").to_s) - exit - end - - def plugin_finalize; end - - # Following methods are not required - # - def connect!; end - - def register_client; end - - def render_template; end - - def upload_bootstrap(content); end - - def perform_bootstrap(bootstrap_path); end - - #### run() execution ends #### - - def set_ext_params - begin - ui.info "Looking for the server #{@name_args[0]}..." - server = service.find_server( - name: @name_args[0], - azure_dns_name: config[:azure_dns_name] - ) - - ## if azure_dns_name value not passed by user then set it using the hostedservicename attribute from the retrieved server's object ## - config[:azure_dns_name] = server.hostedservicename if config[:azure_dns_name].nil? && (server.instance_of? Azure::Role) - unless server.instance_of? Azure::Role - if server.nil? - if !config[:azure_dns_name].nil? - raise "Hosted service #{config[:azure_dns_name]} does not exist." - else - raise "Server #{@name_args[0]} does not exist." - end - else - raise "Server #{@name_args[0]} does not exist under the hosted service #{config[:azure_dns_name]}." - end - end - - ui.info "\nServer #{@name_args[0]} found." - ui.info "Setting the Chef Extension parameters." - ext_params = {} - case server.os_type.downcase - when "windows" - ext_params[:chef_extension] = "ChefClient" - when "linux" - if %w{ubuntu debian rhel centos}.any? { |platform| server.os_version.downcase.include? platform } - ext_params[:chef_extension] = "LinuxChefClient" - else - raise "OS version #{server.os_version} for OS type #{server.os_type} is not supported." - end - else - raise "OS type #{server.os_type} is not supported." - end - - ext_params[:azure_dns_name] = server.hostedservicename || config[:azure_dns_name] - ext_params[:deploy_name] = server.deployname - ext_params[:role_xml] = server.role_xml - ext_params[:azure_vm_name] = @name_args[0] - ext_params[:chef_extension_publisher] = get_chef_extension_publisher - ext_params[:chef_extension_version] = get_chef_extension_version(ext_params[:chef_extension]) - ext_params[:chef_extension_public_param] = get_chef_extension_public_params - ext_params[:chef_extension_private_param] = get_chef_extension_private_params - rescue => error - ui.error("#{error.message}") - Chef::Log.debug("#{error.backtrace.join("\n")}") - exit - end - - ext_params - end - - def wait_until_extension_available(extension_deploy_start_time, extension_availability_wait_timeout) - extension_availability_wait_time = ((Time.now - extension_deploy_start_time) / 60).round - if extension_availability_wait_time <= extension_availability_wait_timeout - ## extension availability wait time has not exceeded the maximum threshold set for the wait timeout ## - my_role = nil - sleep_and_wait = false - deployment = fetch_deployment - if deployment.at_css("Deployment Name") != nil - role_list_xml = deployment.css("RoleInstanceList RoleInstance") - ## list of roles found under the deployment ## - role_list_xml.each do |role| - ## search in the roles list for the given role ## - if role.at_css("RoleName").text == @name_args[0] - my_role = role - break - end - end - - if my_role && my_role.at_css("GuestAgentStatus Status").text == "Ready" - ## given role found and also GuestAgent is ready ## - extension = fetch_extension(my_role) - ## check if Chef Extension not found (which means it is not available/ready yet) then sleep_and_wait OR - ## if found (which means it is available/ready now) then proceed further with chef-client run logs fetch process ## - sleep_and_wait = true if extension.nil? - else - ## given role not found or GuestAgent not ready yet ## - sleep_and_wait = true - end - else - ## deployment could not be found ## - sleep_and_wait = true - end - - ## wait for some time and then re-fetch the status ## - if sleep_and_wait == true - print "#{ui.color(".", :bold)}" - sleep 30 - wait_until_extension_available( - extension_deploy_start_time, - extension_availability_wait_timeout - ) - end - else - ## extension availability wait time exceeded maximum threshold set for the wait timeout ## - raise "\nUnable to fetch chef-client run logs as Chef Extension seems to be unavailable even after #{extension_availability_wait_timeout} minutes of its deployment.\n" - end - end - end - end -end diff --git a/lib/chef/knife/helpers/azure_base.rb b/lib/chef/knife/helpers/azure_base.rb deleted file mode 100644 index 19ac77b7..00000000 --- a/lib/chef/knife/helpers/azure_base.rb +++ /dev/null @@ -1,392 +0,0 @@ -# Author:: Barry Davis (barryd@jetstreamsoftware.com) -# Author:: Seth Chisamore () -# Copyright:: Copyright (c) Chef Software 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. -# - -require "chef/knife" - -class Chef - class Knife - module AzureBase - # :nodoc: - # Would prefer to do this in a rational way, but can't be done b/c of - # Mixlib::CLI's design :( - def self.included(includer) - includer.class_eval do - deps do - require "readline" - require "chef/json_compat" - require_relative "../../../azure/service_management/ASM_interface" - end - - option :azure_subscription_id, - short: "-S ID", - long: "--azure-subscription-id ID", - description: "Your Azure subscription ID" - - option :azure_mgmt_cert, - short: "-p FILENAME", - long: "--azure-mgmt-cert FILENAME", - description: "Your Azure PEM file name" - - option :azure_api_host_name, - short: "-H HOSTNAME", - long: "--azure-api-host-name HOSTNAME", - description: "Your Azure host name" - - option :verify_ssl_cert, - long: "--verify-ssl-cert", - description: "Verify SSL Certificates for communication over HTTPS", - boolean: true, - default: false - - option :azure_publish_settings_file, - long: "--azure-publish-settings-file FILENAME", - description: "Your Azure Publish Settings File" - end - end - - def is_image_windows? - images = service.list_images - target_image = images.select { |i| i.name == config[:azure_source_image] } - if target_image[0].nil? - ui.error('Invalid image. Use the command "knife azure image list" to verify the image name') - exit 1 - else - target_image[0].os == "Windows" - end - end - - def service - @service ||= begin - service = Azure::ServiceManagement::ASMInterface.new( - azure_subscription_id: config[:azure_subscription_id], - azure_mgmt_cert: config[:azure_mgmt_cert], - azure_api_host_name: config[:azure_api_host_name], - verify_ssl_cert: config[:verify_ssl_cert] - ) - end - @service.ui = ui - @service - end - - def msg_pair(label, value, color = :cyan) - if value && !value.to_s.empty? - puts "#{ui.color(label, color)}: #{value}" - end - end - - def msg_server_summary(server) - puts "\n" - msg_pair("DNS Name", server.hostedservicename + ".cloudapp.net") - msg_pair("VM Name", server.name) - msg_pair("Size", server.size) - msg_pair("Azure Source Image", config[:azure_source_image]) - msg_pair("Azure Service Location", config[:azure_service_location]) - msg_pair("Public Ip Address", server.publicipaddress) - msg_pair("Private Ip Address", server.ipaddress) - msg_pair("SSH Port", server.sshport) unless server.sshport.nil? - msg_pair("WinRM Port", server.winrmport) unless server.winrmport.nil? - msg_pair("TCP Ports", server.tcpports) unless server.tcpports.nil? || server.tcpports.empty? - msg_pair("UDP Ports", server.udpports) unless server.udpports.nil? || server.udpports.empty? - msg_pair("Environment", config[:environment] || "_default") - msg_pair("Runlist", config[:run_list]) unless config[:run_list].empty? - puts "\n" - end - - def pretty_key(key) - key.to_s.tr("_", " ").gsub(/\w+/) { |w| w =~ /(ssh)|(aws)/i ? w.upcase : w.capitalize } - end - - # validate command pre-requisites (cli options) - # (config[:connection_password].length <= 6 && config[:connection_password].length >= 72) - def validate_params! - if config[:connection_password] && !config[:connection_password].length.between?(6, 72) - ui.error("The supplied connection password must be 6-72 characters long and meet password complexity requirements") - exit 1 - end - - if config[:azure_connect_to_existing_dns] && config[:azure_vm_name].nil? - ui.error("Specify the VM name using --azure-vm-name option, since you are connecting to existing dns") - exit 1 - end - - unless !!config[:azure_service_location] ^ !!config[:azure_affinity_group] - ui.error("Specify either --azure-service-location or --azure-affinity-group") - exit 1 - end - - unless service.valid_image?(config[:azure_source_image]) - ui.error("Image '#{config[:azure_source_image]}' is invalid") - exit 1 - end - - # Validate join domain requirements. - if config[:azure_domain_name] || config[:azure_domain_user] - if config[:azure_domain_user].nil? || config[:azure_domain_passwd].nil? - ui.error("Must specify both --azure-domain-user and --azure-domain-passwd.") - exit 1 - end - end - - if config[:winrm_ssl] && config[:thumbprint].nil? && config[:winrm_no_verify_cert].nil? - ui.error("The SSL transport was specified without the --thumbprint option. Specify a thumbprint, or alternatively set the --winrm-no-verify-cert option to skip verification.") - exit 1 - end - - if config[:extended_logs] && config[:connection_protocol] != "cloud-api" - ui.error("--extended-logs option only works with --bootstrap-protocol cloud-api") - exit 1 - end - - if config[:connection_protocol] == "cloud-api" && config[:azure_vm_name].nil? && config[:azure_dns_name].nil? - ui.error("Specifying the DNS name using --azure-dns-name or VM name using --azure-vm-name option is required with --bootstrap-protocol cloud-api") - exit 1 - end - - if config[:daemon] - unless is_image_windows? - raise ArgumentError, "The daemon option is only supported for Windows nodes." - end - - unless config[:connection_protocol] == "cloud-api" - raise ArgumentError, "The --daemon option requires the use of --bootstrap-protocol cloud-api" - end - - unless %w{none service task}.include?(config[:daemon].downcase) - raise ArgumentError, "Invalid value for --daemon option. Valid values are 'none', 'service' and 'task'." - end - end - end - - # validates keys - def validate!(keys) - errors = [] - keys.each do |k| - if config[k].nil? - errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your knife.rb or pass as an option." - end - end - exit 1 if errors.each { |e| ui.error(e) }.any? - end - - # validate ASM mandatory keys - def validate_asm_keys!(*keys) - mandatory_keys = %i{azure_subscription_id azure_mgmt_cert azure_api_host_name} - keys.concat(mandatory_keys) - - unless config[:azure_mgmt_cert].nil? - config[:azure_mgmt_cert] = File.read find_file(config[:azure_mgmt_cert]) - end - - if !config[:azure_publish_settings_file].nil? - parse_publish_settings_file(config[:azure_publish_settings_file]) - elsif config[:azure_subscription_id].nil? && config[:azure_mgmt_cert].nil? && config[:azure_api_host_name].nil? - azureprofile_file = get_azure_profile_file_path - if File.exist?(File.expand_path(azureprofile_file)) - errors = parse_azure_profile(azureprofile_file, errors) - end - end - validate!(keys) - end - - def parse_publish_settings_file(filename) - require "nokogiri" unless defined?(Nokogiri) - require "base64" unless defined?(Base64) - require "openssl" unless defined?(OpenSSL) - require "uri" unless defined?(URI) - begin - doc = Nokogiri::XML(File.open(find_file(filename))) - profile = doc.at_css("PublishProfile") - subscription = profile.at_css("Subscription") - # check given PublishSettings XML file format.Currently PublishSettings file have two different XML format - if profile.attribute("SchemaVersion").nil? - management_cert = OpenSSL::PKCS12.new(Base64.decode64(profile.attribute("ManagementCertificate").value)) - config[:azure_api_host_name] = URI(profile.attribute("Url").value).host - elsif profile.attribute("SchemaVersion").value == "2.0" - management_cert = OpenSSL::PKCS12.new(Base64.decode64(subscription.attribute("ManagementCertificate").value)) - config[:azure_api_host_name] = URI(subscription.attribute("ServiceManagementUrl").value).host - else - ui.error("Publish settings file Schema not supported - " + filename) - end - config[:azure_mgmt_cert] = management_cert.certificate.to_pem + management_cert.key.to_pem - config[:azure_subscription_id] = doc.at_css("Subscription").attribute("Id").value - rescue - ui.error("Incorrect publish settings file - " + filename) - exit 1 - end - end - - def get_azure_profile_file_path - "~/.azure/azureProfile.json" - end - - def parse_azure_profile(filename, errors) - require "openssl" unless defined?(OpenSSL) - require "uri" unless defined?(URI) - errors = [] if errors.nil? - azure_profile = File.read(File.expand_path(filename)) - azure_profile = JSON.parse(azure_profile) - default_subscription = get_default_subscription(azure_profile) - if default_subscription.key?("id") && default_subscription.key?("managementCertificate") && default_subscription.key?("managementEndpointUrl") - - config[:azure_subscription_id] = default_subscription["id"] - mgmt_key = OpenSSL::PKey::RSA.new(default_subscription["managementCertificate"]["key"]).to_pem - mgmt_cert = OpenSSL::X509::Certificate.new(default_subscription["managementCertificate"]["cert"]).to_pem - config[:azure_mgmt_cert] = mgmt_key + mgmt_cert - config[:azure_api_host_name] = URI(default_subscription["managementEndpointUrl"]).host - else - errors << "Check if values set for 'id', 'managementCertificate', 'managementEndpointUrl' in -> #{filename} for 'defaultSubscription'. \n OR " - end - errors - end - - def get_default_subscription(azure_profile) - first_subscription_as_default = nil - azure_profile["subscriptions"].each do |subscription| - if subscription["isDefault"] - Chef::Log.info("Default subscription \'#{subscription["name"]}\'' selected.") - return subscription - end - - first_subscription_as_default ||= subscription - end - - if first_subscription_as_default - Chef::Log.info("First subscription \'#{subscription["name"]}\' selected as default.") - else - Chef::Log.info("No subscriptions found.") - exit 1 - end - first_subscription_as_default - end - - def find_file(name) - name = ::File.expand_path(name) - config_dir = Chef::Knife.chef_config_dir - if File.exist? name - file = name - elsif config_dir && File.exist?(File.join(config_dir, name)) - file = File.join(config_dir, name) - elsif File.exist?(File.join(ENV["HOME"], ".chef", name)) - file = File.join(ENV["HOME"], ".chef", name) - else - ui.error("Unable to find file - " + name) - exit 1 - end - file - end - - def fetch_deployment - deployment_name = service.deployment_name(config[:azure_dns_name]) - service.deployment("hostedservices/#{config[:azure_dns_name]}/deployments/#{deployment_name}") - end - - def fetch_role - deployment = fetch_deployment - - if deployment.at_css("Deployment Name") != nil - role_list_xml = deployment.css("RoleInstanceList RoleInstance") - role_list_xml.each do |role| - if role.at_css("RoleName").text == (config[:azure_vm_name] || @name_args[0]) - return role - end - end - end - nil - end - - def fetch_extension(role) - ext_list_xml = role.css("ResourceExtensionStatusList ResourceExtensionStatus") - return nil if ext_list_xml.nil? - - ext_list_xml.each do |ext| - if ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.LinuxChefClient" || ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.ChefClient" - return ext - end - end - nil - end - - def fetch_substatus(extension) - return nil if extension.at_css("ExtensionSettingStatus SubStatusList SubStatus").nil? - - substatus_list_xml = extension.css("ExtensionSettingStatus SubStatusList SubStatus") - substatus_list_xml.each do |substatus| - if substatus.at_css("Name").text == "Chef Client run logs" - return substatus - end - end - nil - end - - def fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout) - ## fetch server details ## - role = fetch_role - if !role.nil? - ## fetch Chef Extension details deployed on the server ## - ext = fetch_extension(role) - if !ext.nil? - ## fetch substatus field which contains the chef-client run logs ## - substatus = fetch_substatus(ext) - if !substatus.nil? - ## chef-client run logs becomes available ## - name = substatus.at_css("Name").text - status = substatus.at_css("Status").text - message = substatus.at_css("Message").text - - ## printing the logs ## - puts "\n\n******** Please find the chef-client run details below ********\n\n" - print "----> chef-client run status: " - case status - when "Success" - ## chef-client run succeeded ## - color = :green - when "Error" - ## chef-client run failed ## - color = :red - when "Transitioning" - ## chef-client run did not complete within maximum timeout of 30 minutes ## - ## fetch whatever logs available under the chef-client.log file ## - color = :yellow - end - puts "#{ui.color(status, color, :bold)}" - puts "----> chef-client run logs: " - puts "\n#{message}\n" ## message field of substatus contains the chef-client run logs ## - else - ## unavailability of the substatus field indicates that chef-client run is not completed yet on the server ## - fetch_process_wait_time = ((Time.now - fetch_process_start_time) / 60).round - if fetch_process_wait_time <= fetch_process_wait_timeout ## wait for maximum 30 minutes until chef-client run logs becomes available ## - print "#{ui.color(".", :bold)}" - sleep 30 - fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout) - else - ## wait time exceeded maximum threshold set for the wait timeout ## - ui.error "\nchef-client run logs could not be fetched since fetch process exceeded wait timeout of #{fetch_process_wait_timeout} minutes.\n" - end - end - else - ## Chef Extension could not be found ## - ui.error("Unable to find Chef extension under role #{config[:azure_vm_name] || @name_args[0]}.") - end - else - ## server could not be found ## - ui.error("chef-client run logs could not be fetched since role #{config[:azure_vm_name] || @name_args[0]} could not be found.") - end - end - end - end -end diff --git a/lib/chef/knife/helpers/azurerm_base.rb b/lib/chef/knife/helpers/azurerm_base.rb index 3fa9b08d..a4a8ff2c 100644 --- a/lib/chef/knife/helpers/azurerm_base.rb +++ b/lib/chef/knife/helpers/azurerm_base.rb @@ -275,10 +275,10 @@ def validate_params! end if !is_image_windows? - if (config[:azure_vm_name].match /^(?=.*[a-zA-Z-])([a-zA-z0-9-]{1,64})$/).nil? + if (config[:azure_vm_name].match(/^(?=.*[a-zA-Z-])([a-zA-z0-9-]{1,64})$/)).nil? raise ArgumentError, "VM name can only contain alphanumeric and hyphen(-) characters and maximum length cannot exceed 64 characters." end - elsif (config[:azure_vm_name].match /^(?=.*[a-zA-Z-])([a-zA-z0-9-]{1,15})$/).nil? + elsif (config[:azure_vm_name].match(/^(?=.*[a-zA-Z-])([a-zA-z0-9-]{1,15})$/)).nil? raise ArgumentError, "VM name can only contain alphanumeric and hyphen(-) characters and maximum length cannot exceed 15 characters." end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2f8976dd..7cd693e3 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,23 +1,9 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) -require "chef/knife/azure_ag_create" -require "chef/knife/azure_ag_list" -require "chef/knife/azure_image_list" -require "chef/knife/azure_internal-lb_create" -require "chef/knife/azure_internal-lb_list" -require "chef/knife/azure_server_create" -require "chef/knife/azure_server_delete" -require "chef/knife/azure_server_show" -require "chef/knife/azure_server_list" -require "chef/knife/azure_vnet_create" -require "chef/knife/azure_vnet_list" - require "chef/knife/azurerm_server_list" require "chef/knife/azurerm_server_show" require "chef/knife/azurerm_server_delete" require "chef/knife/azurerm_server_create" -require "chef/knife/bootstrap_azure" - require "chef/knife/bootstrap_azurerm" if Chef::Platform.windows? @@ -60,11 +46,11 @@ def self.from(system_exit) end c.around(:example) do |ex| - begin - ex.run - rescue SystemExit => e - raise UnexpectedSystemExit.from(e) - end + + ex.run + rescue SystemExit => e + raise UnexpectedSystemExit.from(e) + end end @@ -93,8 +79,8 @@ def is_config_present unset_env_var = [] unset_config_options = [] is_config = true - config_file_exist = File.exist?(File.expand_path("../integration/config/environment.yml", __FILE__)) - azure_config = YAML.load(File.read(File.expand_path("../integration/config/environment.yml", __FILE__))) if config_file_exist + config_file_exist = File.exist?(File.expand_path("integration/config/environment.yml", __dir__)) + azure_config = YAML.load(File.read(File.expand_path("integration/config/environment.yml", __dir__))) if config_file_exist %w{AZURE_PUBLISH_SETTINGS_FILE AZURE_MGMT_CERT AZURE_SUBSCRIPTION_ID AZURE_API_HOST_NAME}.each do |az_env_var| if ENV[az_env_var].nil? @@ -154,8 +140,8 @@ def init_azure_test puts "Error while creating file - azure invalid" end - config_file_exist = File.exist?(File.expand_path("../integration/config/environment.yml", __FILE__)) - azure_config = YAML.load(File.read(File.expand_path("../integration/config/environment.yml", __FILE__))) if config_file_exist + config_file_exist = File.exist?(File.expand_path("integration/config/environment.yml", __dir__)) + azure_config = YAML.load(File.read(File.expand_path("integration/config/environment.yml", __dir__))) if config_file_exist %w{AZ_SSH_USER AZ_SSH_PASSWORD AZ_WINDOWS_SSH_USER AZ_WINDOWS_SSH_PASSWORD AZ_WINRM_USER AZ_WINRM_PASSWORD AZ_LINUX_IMAGE AZ_LINUX_VM_SIZE AZ_INVALID_VM_SIZE AZ_WINDOWS_VM_SIZE AZ_WINDOWS_IMAGE AZ_WINDOWS_SSH_IMAGE AZURE_SERVICE_LOCATION}.each do |az_config_opt| instance_variable_set("@#{az_config_opt.downcase}", (azure_config[az_config_opt] if azure_config) || ENV[az_config_opt]) diff --git a/spec/unit/ags_spec.rb b/spec/unit/ags_spec.rb deleted file mode 100644 index 43a0fa70..00000000 --- a/spec/unit/ags_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -describe "ags" do - include AzureSpecHelper - include QueryAzureMock - - before "setup connection" do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure (@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - context "mock with actually retrieved values" do - it "should find strings" do - items = @connection.ags.all - expect(items.length).to be > 1 - items.each do |ag| - expect(ag.name).not_to be_nil - expect(ag.label).not_to be_nil - expect(ag.location).not_to be_nil - end - end - it "should contain West US ag" do - items = @connection.ags.all - found_us = false - items.each do |item| - found_us = true if item.location == "West US" - end - expect(found_us).to be true - end - end - - context "create a new affinity group" do - it "using explicitly parameters it should pass in expected body" do - params = { - azure_ag_name: "new-ag", - azure_ag_desc: "ag description", - azure_location: "West US", - } - @connection.ags.create(params) - expect(@postname).to eq("affinitygroups") - expect(@postverb).to eq("post") - expect(@postbody).to eq(readFile("create_ag_for_new-ag.xml")) - end - end -end diff --git a/spec/unit/azure_ag_create_spec.rb b/spec/unit/azure_ag_create_spec.rb deleted file mode 100644 index f47b335e..00000000 --- a/spec/unit/azure_ag_create_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureAgCreate do - include AzureSpecHelper - include QueryAzureMock - - before do - @server_instance = create_instance(Chef::Knife::AzureAgCreate) - @connection = @server_instance.service.connection - stub_query_azure(@connection) - allow(@server_instance).to receive(:puts) - allow(@server_instance).to receive(:print) - end - - it "should fail missing args." do - expect(@connection.ags).to_not receive(:create) - expect(@server_instance.ui).to receive(:error).twice - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "should succeed." do - @server_instance.config[:azure_affinity_group] = "new-ag" - @server_instance.config[:azure_service_location] = "West US" - expect(@connection.ags).to receive(:create).with( - azure_ag_name: "new-ag", - azure_ag_desc: nil, - azure_location: "West US" - ).and_call_original - expect(@server_instance.ui).to_not receive(:warn) - expect(@server_instance.ui).to_not receive(:error) - @server_instance.merge_configs - @server_instance.run - end -end diff --git a/spec/unit/azure_ag_list_spec.rb b/spec/unit/azure_ag_list_spec.rb deleted file mode 100644 index c15da865..00000000 --- a/spec/unit/azure_ag_list_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureAgList do - include AzureSpecHelper - include QueryAzureMock - before do - @server_instance = Chef::Knife::AzureAgList.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - azure_service_location: "West Europe", - azure_source_image: "SUSE__SUSE-Linux-Enterprise-Server-11SP2-20120521-en-us-30GB.vhd", - azure_dns_name: "service001", - azure_vm_name: "vm01", - azure_storage_account: "ka001testeurope", - azure_vm_size: "Small", - }.each do |key, value| - @server_instance.config[key] = value - end - stub_query_azure(@server_instance.service.connection) - allow(@server_instance).to receive(:items).and_return(:true) - allow(@server_instance).to receive(:puts) - end - - it "should display Name, Location, and Description columns." do - expect(@server_instance.ui).to receive(:list).with( - ["Name", "Location", "Description", - "agname", "West US", "agdesc", - "jm-affinity-group", "West US", "", - "test", "West US", "testdesc" - ], - :uneven_columns_across, - 3 - ) - @server_instance.run - end - -end diff --git a/spec/unit/azure_base_spec.rb b/spec/unit/azure_base_spec.rb deleted file mode 100644 index f94eb617..00000000 --- a/spec/unit/azure_base_spec.rb +++ /dev/null @@ -1,248 +0,0 @@ -# -# Author:: Ameya Varade () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" - -describe Chef::Knife::AzureBase do - include AzureSpecHelper - class Chef - class Knife - class DummyClass < Knife - include Knife::AzureBase - end - end - end - before do - @dummy = Chef::Knife::DummyClass.new - @dummy.config[:azure_api_host_name] = "preview.core.windows-int.net" - @dummy.config[:azure_subscription_id] = "azure_subscription_id" - @dummy.config[:azure_mgmt_cert] = @cert_file - allow(@dummy.ui).to receive(:error) - end - describe "azure base tests - " do - context "Tests for publish settings file" do - before do - @dummy.config[:azure_api_host_name] = nil - @dummy.config[:azure_subscription_id] = nil - end - - def validate_cert - expect(@dummy.config[:azure_mgmt_cert]).to include("-----BEGIN CERTIFICATE-----") - expect(@dummy.config[:azure_mgmt_cert]).to include("-----END CERTIFICATE-----") - expect(@dummy.config[:azure_mgmt_cert]).to include("-----BEGIN RSA PRIVATE KEY-----") - expect(@dummy.config[:azure_mgmt_cert]).to include("-----END RSA PRIVATE KEY-----") - end - - it "- should continue to regular flow if publish settings file not provided" do - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/C_account_azure_arm_profile.json") - @dummy.config[:azure_api_host_name] = "preview.core.windows-int.net" - @dummy.config[:azure_subscription_id] = "azure_subscription_id" - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to be == "preview.core.windows-int.net" - expect(@dummy.config[:azure_subscription_id]).to be == "azure_subscription_id" - end - - it "- should validate extract parameters" do - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("azureValid.publishsettings") - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to be == "management.core.windows.net" - expect(@dummy.config[:azure_subscription_id]).to be == "id1" - validate_cert - end - - it "- should validate parse method" do - @dummy.parse_publish_settings_file(get_publish_settings_file_path("azureValid.publishsettings")) - expect(@dummy.config[:azure_api_host_name]).to be == "management.core.windows.net" - expect(@dummy.config[:azure_subscription_id]).to be == "id1" - validate_cert - end - - it "- should validate parse method for SchemaVersion2-0 publishsettings file" do - @dummy.parse_publish_settings_file(get_publish_settings_file_path("azureValidSchemaVersion-2.0.publishsettings")) - expect(@dummy.config[:azure_api_host_name]).to be == "management.core.windows.net" - expect(@dummy.config[:azure_subscription_id]).to be == "id1" - validate_cert - end - - it "- should validate settings file and subscription id" do - @dummy.config[:azure_subscription_id] = "azure_subscription_id" - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("azureValid.publishsettings") - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to be == "management.core.windows.net" - expect(@dummy.config[:azure_subscription_id]).to be == "id1" - validate_cert - end - - it "- should raise error if invalid publish settings provided" do - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("azureInvalid.publishsettings") - expect { @dummy.validate_asm_keys! }.to raise_error(SystemExit) - end - - it "- should raise error if publish settings file does not exists" do - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("azureNotAvailable.publishsettings") - expect { @dummy.validate_asm_keys! }.to raise_error(SystemExit) - end - end - end - - describe "azure base tests - for azure profile" do - before(:each) do - @dummy.config[:azure_mgmt_cert] = nil - end - - context "when publishSettings file specified in knife.rb has A account and azureProfile file has B account" do - before do - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("A_account.publishsettings") - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/B_account_azure_profile.json") - end - - it "selects A account of publishSettings file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("A.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("A_subscription_id") - end - end - - context "when publishSettings file specified in knife.rb has B account and azureProfile file has A account" do - before do - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("B_account.publishsettings") - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/A_account_azure_profile.json") - @dummy.config[:azure_api_host_name] = "preview.core.windows-int.net" - @dummy.config[:azure_subscription_id] = "azure_subscription_id" - @dummy.config[:azure_mgmt_cert] = @cert_file - end - - it "selects B account of publishSettings file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("B.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("B_subscription_id") - end - end - - context "when publishSettings file is not specified in knife.rb and azureProfile file has A account" do - before do - @dummy.config[:azure_publish_settings_file] = nil - @dummy.config[:azure_api_host_name] = nil - @dummy.config[:azure_subscription_id] = nil - @dummy.config[:azure_mgmt_cert] = nil - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/A_account_azure_profile.json") - end - - it "selects A account of azureProfile file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("A.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("A_subscription_id") - end - end - - context "when publishSettings file is not specified in knife.rb and azureProfile file has B account" do - before do - @dummy.config[:azure_publish_settings_file] = nil - - @dummy.config[:azure_api_host_name] = nil - @dummy.config[:azure_subscription_id] = nil - @dummy.config[:azure_mgmt_cert] = nil - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/B_account_azure_profile.json") - end - - it "selects B account of azureProfile file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("B.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("B_subscription_id") - end - end - - context "when neither publishSettings file is specified in knife.rb nor azureProfile file exist" do - before do - @dummy.config[:azure_publish_settings_file] = nil - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/C_account_azure_profile.json") - end - - it "gives error and exits" do - expect { @dummy.validate_asm_keys! }.to raise_error SystemExit - end - end - - context "when publishSettings file is not specified in knife.rb and azureProfile file has both A and B account with B as the default account" do - before do - @dummy.config[:azure_publish_settings_file] = nil - - @dummy.config[:azure_api_host_name] = nil - @dummy.config[:azure_subscription_id] = nil - @dummy.config[:azure_mgmt_cert] = nil - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/A_Bd_account_azure_profile.json") - end - - it "selects B account of azureProfile file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("B.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("B_subscription_id") - end - end - - context "when publishSettings file is not specified in knife.rb and azureProfile file has both A and B account with A as the default account" do - before do - @dummy.config[:azure_publish_settings_file] = nil - @dummy.config[:azure_api_host_name] = nil - @dummy.config[:azure_subscription_id] = nil - @dummy.config[:azure_mgmt_cert] = nil - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/Ad_B_account_azure_profile.json") - end - - it "selects A account of azureProfile file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("A.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("A_subscription_id") - end - end - - context "when publishSettings file specified in knife.rb has A account and azureProfile file has both A and B account with B as the default account" do - before do - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("A_account.publishsettings") - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/A_Bd_account_azure_profile.json") - end - - it "selects A account of publishSettings file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("A.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("A_subscription_id") - end - end - - context "when publishSettings file specified in knife.rb has B account and azureProfile file has both A and B account with A as the default account" do - before do - @dummy.config[:azure_publish_settings_file] = get_publish_settings_file_path("B_account.publishsettings") - allow(@dummy).to receive(:get_azure_profile_file_path).and_return(File.dirname(__FILE__) + "/assets/azure-profile-files/Ad_B_account_azure_profile.json") - end - - it "selects B account of publishSettings file" do - @dummy.validate_asm_keys! - expect(@dummy.config[:azure_api_host_name]).to eq("B.endpoint.net") - expect(@dummy.config[:azure_subscription_id]).to eq("B_subscription_id") - end - end - - context "find_file" do - it "finds the file with given path" do - file_path = get_publish_settings_file_path("azureValid.publishsettings") - @dummy.config[:azure_publish_settings_file] = file_path - expect(@dummy.find_file(@dummy.config[:azure_publish_settings_file])).to eq file_path - end - end - end -end diff --git a/spec/unit/azure_image_list_spec.rb b/spec/unit/azure_image_list_spec.rb deleted file mode 100644 index 4d8f5b5e..00000000 --- a/spec/unit/azure_image_list_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" -require "pry" - -describe Chef::Knife::AzureImageList do - include AzureSpecHelper - include QueryAzureMock - before do - @server_instance = Chef::Knife::AzureImageList.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - azure_service_location: "West Europe", - azure_source_image: "SUSE__SUSE-Linux-Enterprise-Server-11SP2-20120521-en-us-30GB.vhd", - azure_dns_name: "service001", - azure_vm_name: "vm01", - azure_storage_account: "ka001testeurope", - azure_vm_size: "Small", - }.each do |key, value| - @server_instance.config[key] = value - end - stub_query_azure(@server_instance.service.connection) - allow(@server_instance).to receive(:items).and_return(:true) - allow(@server_instance).to receive(:puts) - end - - it "should display only Name OS and Location columns." do - expect(@server_instance.ui).to receive(:list) - .with(["Name", "OS", "Location", "CANONICAL__Canonical-Ubuntu-12-04-20120519-2012-05-19-en-us-30GB.vhd", - "Linux", "East Asia", "MSFT__Windows-Server-2008-R2-SP1.11-29-2011", "Windows", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-2008-R2-SP1-with-SQL-Server-2012-Eval.11-29-2011", - "Windows", "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-8-Beta.en-us.30GB.2012-03-22", - "Windows", "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-8-Beta.2-17-2012", "Windows", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-2008-R2-SP1.en-us.30GB.2012-3-22", "Windows", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "OpenLogic__OpenLogic-CentOS-62-20120509-en-us-30GB.vhd", "Linux", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "SUSE__SUSE-Linux-Enterprise-Server-11SP2-20120521-en-us-30GB.vhd", "Linux", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "SUSE__OpenSUSE64121-03192012-en-us-15GB.vhd", "Linux", - "East Asia, West US"], :uneven_columns_across, 3) - @server_instance.run - end - - it "should display Name, Category, Label and OS columns when show_all_fields set to true." do - @server_instance.config[:show_all_fields] = true - expect(@server_instance.ui).to receive(:list) - .with(["Name", "Category", "Label", "OS", "Location", - "CANONICAL__Canonical-Ubuntu-12-04-20120519-2012-05-19-en-us-30GB.vhd", "Canonical", - "Ubuntu Server 12.04 20120519", "Linux", "East Asia", "MSFT__Windows-Server-2008-R2-SP1.11-29-2011", - "Microsoft", "Windows Server 2008 R2 SP1, Nov 2011", - "Windows", "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-2008-R2-SP1-with-SQL-Server-2012-Eval.11-29-2011", "Microsoft", - "SQL Server 2012 Evaluation, Nov 2011", "Windows", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-8-Beta.en-us.30GB.2012-03-22", "Microsoft", "Windows Server 8 Beta, Mar 2012", - "Windows", "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-8-Beta.2-17-2012", "Microsoft", "Windows Server 8 Beta, Feb 2012", - "Windows", "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "MSFT__Windows-Server-2008-R2-SP1.en-us.30GB.2012-3-22", "Microsoft", - "Windows Server 2008 R2 SP1, Mar 2012", "Windows", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "OpenLogic__OpenLogic-CentOS-62-20120509-en-us-30GB.vhd", "OpenLogic", "CentOS 6.2 provided by OpenLogic", "Linux", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "SUSE__SUSE-Linux-Enterprise-Server-11SP2-20120521-en-us-30GB.vhd", "SUSE", "SUSE Linux Enterprise Server", "Linux", - "East Asia, Southeast Asia, North Europe, West Europe, East US, West US", - "SUSE__OpenSUSE64121-03192012-en-us-15GB.vhd", "SUSE", "OpenSUSE64-12.1-Beta", "Linux", - "East Asia, West US"], :uneven_columns_across, 5) - @server_instance.run - end -end diff --git a/spec/unit/azure_internal-lb_create_spec.rb b/spec/unit/azure_internal-lb_create_spec.rb deleted file mode 100644 index 62fc9f8d..00000000 --- a/spec/unit/azure_internal-lb_create_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureInternalLbCreate do - include AzureSpecHelper - include QueryAzureMock - - before do - @server_instance = create_instance(Chef::Knife::AzureInternalLbCreate) - @connection = @server_instance.service.connection - stub_query_azure(@connection) - allow(@server_instance).to receive(:puts) - allow(@server_instance).to receive(:print) - end - - it "should fail missing args." do - expect(@connection.lbs).to_not receive(:create) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "should succeed." do - @server_instance.config[:azure_load_balancer] = "new-lb" - @server_instance.config[:azure_lb_static_vip] = "10.3.3.3" - @server_instance.config[:azure_subnet_name] = "vnet" - @server_instance.config[:azure_dns_name] = "vmname" - expect(@server_instance.service.connection.lbs).to receive(:create).with( - azure_load_balancer: "new-lb", - azure_lb_static_vip: "10.3.3.3", - azure_subnet_name: "vnet", - azure_dns_name: "vmname" - ).and_call_original - expect(@server_instance.ui).to_not receive(:warn) - expect(@server_instance.ui).to_not receive(:error) - @server_instance.run - end -end diff --git a/spec/unit/azure_internal-lb_list_spec.rb b/spec/unit/azure_internal-lb_list_spec.rb deleted file mode 100644 index 6f7758ba..00000000 --- a/spec/unit/azure_internal-lb_list_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureInternalLbList do - include AzureSpecHelper - include QueryAzureMock - before do - @server_instance = Chef::Knife::AzureInternalLbList.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - azure_service_location: "West Europe", - azure_source_image: "SUSE__SUSE-Linux-Enterprise-Server-11SP2-20120521-en-us-30GB.vhd", - azure_dns_name: "service001", - azure_vm_name: "vm01", - azure_storage_account: "ka001testeurope", - azure_vm_size: "Small", - }.each do |key, value| - @server_instance.config[key] = value - end - stub_query_azure(@server_instance.service.connection) - allow(@server_instance).to receive(:items).and_return(:true) - allow(@server_instance).to receive(:puts) - end - - it "should display Name, Service, Subnet, and VIP columns." do - expect(@server_instance.ui).to receive(:list).with( - ["Name", "Service", "Subnet", "VIP", - "lb_name", "service001", "vnet1", "10.4.4.4" - ], - :uneven_columns_across, - 4 - ) - @server_instance.run - end - -end diff --git a/spec/unit/azure_server_create_spec.rb b/spec/unit/azure_server_create_spec.rb deleted file mode 100644 index d288031d..00000000 --- a/spec/unit/azure_server_create_spec.rb +++ /dev/null @@ -1,1264 +0,0 @@ -# -# Author:: Mukta Aphale () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" -require "chef/knife/bootstrap" -require "active_support/core_ext/hash/conversions" - -describe Chef::Knife::AzureServerCreate do - include AzureSpecHelper - include QueryAzureMock - include AzureUtility - - before do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - azure_service_location: "West Europe", - azure_source_image: "SUSE__SUSE-Linux-Enterprise-Server-11SP2-20120521-en-us-30GB.vhd", - azure_dns_name: "service001", - azure_vm_name: "vm002", - azure_storage_account: "ka001testeurope", - azure_vm_size: "Small", - connection_user: "test-user", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure @server_instance.service.connection - @connection = @server_instance.service.connection - allow(@server_instance).to receive(:tcp_test_ssh).and_return(true) - allow(@server_instance).to receive(:tcp_test_winrm).and_return(true) - @server_instance.initial_sleep_delay = 0 - allow(@server_instance).to receive(:sleep).and_return(0) - allow(@server_instance).to receive(:puts) - allow(@server_instance).to receive(:print) - allow(@server_instance).to receive(:check_license) - allow(@server_instance).to receive(:connect!) - allow(@server_instance).to receive(:register_client) - allow(@server_instance).to receive(:render_template).and_return "content" - allow(@server_instance).to receive(:upload_bootstrap).with("content").and_return "/remote/path.sh" - allow(@server_instance).to receive(:perform_bootstrap).with("/remote/path.sh") - end - - def test_params(testxml, chef_config, role_name, host_name) - expect(xml_content(testxml, "UserName")).to be == chef_config[:connection_user] - expect(xml_content(testxml, "UserPassword")).to be == chef_config[:connection_password] - expect(xml_content(testxml, "SourceImageName")).to be == chef_config[:azure_source_image] - expect(xml_content(testxml, "RoleSize")).to be == chef_config[:azure_vm_size] - expect(xml_content(testxml, "HostName")).to be == host_name - expect(xml_content(testxml, "RoleName")).to be == role_name - end - - describe "parameter test:" do - context "compulsory parameters" do - it "azure_subscription_id" do - @server_instance.config.delete(:azure_subscription_id) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "azure_mgmt_cert" do - @server_instance.config.delete(:azure_mgmt_cert) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "azure_api_host_name" do - @server_instance.config.delete(:azure_api_host_name) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "azure_source_image" do - @server_instance.config.delete(:azure_source_image) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "azure_vm_size" do - @server_instance.config.delete(:azure_vm_size) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "azure_service_location and azure_affinity_group not allowed" do - @server_instance.config[:azure_affinity_group] = "test-affinity" - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "azure_service_location or azure_affinity_group must be provided" do - @server_instance.config.delete(:azure_service_location) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "raises an error if neither --azure-dns-name or --azure-vm-name are provided" do - @server_instance.config.delete(:azure_dns_name) - @server_instance.config.delete(:azure_vm_name) - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - - context "when winrm authentication protocol invalid" do - it "raise error" do - @server_instance.config[:winrm_auth_method] = "invalide" - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - end - - context "when winrm-ssl ssl and missing thumbprint" do - it "raise error if :winrm_no_verify_cert is not set" do - @server_instance.config[:winrm_ssl] = true - @server_instance.config[:winrm_no_verify_cert] = true - expect(@server_instance.ui).to receive(:error) - expect { @server_instance.run }.to raise_error(SystemExit) - end - end - end - - context "validate parameters" do - it "raise error if daemon option is not provided for windows node" do - @server_instance.config[:daemon] = "service" - expect { @server_instance.run }.to raise_error( - ArgumentError, "The daemon option is only supported for Windows nodes." - ) - end - - it "raises error if invalid value is provided for daemon option" do - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:daemon] = "foo" - @server_instance.config[:connection_protocol] = "cloud-api" - expect { @server_instance.run }.to raise_error( - ArgumentError, "Invalid value for --daemon option. Valid values are 'none', 'service' and 'task'." - ) - end - - it "raises error if connection_protocol is not cloud-api for daemon option" do - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:daemon] = "service" - expect { @server_instance.run }.to raise_error( - ArgumentError, "The --daemon option requires the use of --bootstrap-protocol cloud-api" - ) - end - - it "does not raise error if daemon option value is 'service'" do - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:daemon] = "service" - @server_instance.config[:connection_protocol] = "cloud-api" - expect { @server_instance.run }.not_to raise_error( - ArgumentError, "Invalid value for --daemon option. Use valid daemon values i.e 'none', 'service' and 'task'." - ) - end - - it "does not raise error if daemon option value is 'none'" do - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:daemon] = "none" - @server_instance.config[:connection_protocol] = "cloud-api" - expect { @server_instance.run }.not_to raise_error( - ArgumentError, "Invalid value for --daemon option. Use valid daemon values i.e 'none', 'service' and 'task'." - ) - end - - it "does not raise error if daemon option value is 'task'" do - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:daemon] = "task" - @server_instance.config[:connection_protocol] = "cloud-api" - expect { @server_instance.run }.not_to raise_error( - ArgumentError, "Invalid value for --daemon option. Use valid daemon values i.e 'none', 'service' and 'task'." - ) - end - end - - context "timeout parameters" do - it "uses correct values when not specified" do - expect(@server_instance.options[:azure_vm_startup_timeout][:default].to_i).to eq(10) - expect(@server_instance.options[:azure_vm_ready_timeout][:default].to_i).to eq(15) - end - end - - context "server create options" do - before do - @server_instance.config[:connection_protocol] = "ssh" - @server_instance.config[:connection_user] = "connection_user" - @server_instance.config[:connection_password] = "connection_password" - @server_instance.config.delete(:azure_vm_name) - @server_instance.config.delete(:azure_storage_account) - allow(@server_instance).to receive(:msg_server_summary) - end - - it "quick create" do - expect(@server_instance).to receive(:is_image_windows?).at_least(:twice).and_return(false) - @server_instance.config[:azure_dns_name] = "vmname" # service name to be used as vm name - @server_instance.run - expect(@server_instance.config[:azure_vm_name]).to be == "vmname" - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "MediaLink")).to_not be nil - expect(xml_content(testxml, "DiskName")).to_not be nil - test_params(testxml, @server_instance.config, @server_instance.config[:azure_dns_name], - @server_instance.config[:azure_dns_name]) - end - - it "quick create with wirm - API check" do - expect(@server_instance).to receive(:is_image_windows?).at_least(:twice).and_return(true) - @server_instance.config[:azure_dns_name] = "vmname" # service name to be used as vm name - @server_instance.config[:connection_user] = "opscodechef" - @server_instance.config[:connection_password] = "Opscode123" - @server_instance.config[:connection_protocol] = "winrm" - @server_instance.run - expect(@server_instance.config[:azure_vm_name]).to be == "vmname" - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "WinRM")).to_not be nil - expect(xml_content(testxml, "Listeners")).to_not be nil - expect(xml_content(testxml, "Listener")).to_not be nil - expect(xml_content(testxml, "Protocol")).to be == "Http" - end - - it "generate unique OS DiskName" do - os_disks = [] - allow(@bootstrap).to receive(:run) - allow(@server_instance).to receive(:validate_asm_keys!) - @server_instance.config[:azure_dns_name] = "vmname" - - 5.times do - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - disklink = xml_content(testxml, "MediaLink") - expect(os_disks).to_not include(disklink) - os_disks.push(disklink) - end - end - - it "skip user specified tcp-endpoints if its ports already use by ssh endpoint" do - # Default external port for ssh endpoint is 22. - @server_instance.config[:tcp_endpoints] = "12:22" - expect(@server_instance).to receive(:is_image_windows?).at_least(:twice).and_return(false) - @server_instance.config[:azure_dns_name] = "vmname" # service name to be used as vm name - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - testxml.css('InputEndpoint Protocol:contains("TCP")').each do |port| - # Test data in @server_instance.config[:tcp_endpoints]:=> "12:22" this endpoints external port 22 is already use by ssh endpoint. So it should skip endpoint "12:22". - expect(port.parent.css("LocalPort").text).to_not eq("12") - end - end - - it "advanced create" do - # set all params - @server_instance.config[:azure_dns_name] = "service001" - @server_instance.config[:azure_vm_name] = "vm002" - @server_instance.config[:azure_storage_account] = "ka001testeurope" - @server_instance.config[:azure_os_disk_name] = "os-disk" - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "MediaLink")).to be == "http://ka001testeurope.blob.core.windows-int.net/vhds/os-disk.vhd" - expect(xml_content(testxml, "DiskName")).to be == @server_instance.config[:azure_os_disk_name] - test_params(testxml, @server_instance.config, @server_instance.config[:azure_vm_name], - @server_instance.config[:azure_vm_name]) - end - - it "create with availability set" do - # set all params - @server_instance.config[:azure_dns_name] = "service001" - @server_instance.config[:azure_vm_name] = "vm002" - @server_instance.config[:azure_storage_account] = "ka001testeurope" - @server_instance.config[:azure_os_disk_name] = "os-disk" - @server_instance.config[:azure_availability_set] = "test-availability-set" - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "AvailabilitySetName")).to be == "test-availability-set" - end - - it "server create with virtual network and subnet" do - @server_instance.config[:azure_dns_name] = "vmname" - @server_instance.config[:azure_network_name] = "test-network" - @server_instance.config[:azure_subnet_name] = "test-subnet" - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "SubnetName")).to be == "test-subnet" - end - - it "creates new load balanced endpoints" do - @server_instance.config[:azure_dns_name] = "vmname" - @server_instance.config[:tcp_endpoints] = "80:80:EXTERNAL:lb_set,443:443:EXTERNAL:lb_set_ssl:/healthcheck" # TODO: is this a good way of specifying this? - expect(@server_instance).to receive(:is_image_windows?).at_least(:twice).and_return(false) - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - endpoints = testxml.css("InputEndpoint") - - # Should be 3 endpoints, 1 for SSH, 1 for port 80 using LB set name lb_set, and 1 for port 443 using lb_set2 and with healtcheck path. - expect(endpoints.count).to be == 3 - - # Convert it to a hash as it's easier to test. - eps = [] - endpoints.each do |ep| - eps << Hash.from_xml(ep.to_s) - end - - expect(eps[0]["InputEndpoint"]["Name"]).to be == "SSH" - - lb_set_ep = eps[1]["InputEndpoint"] - expect(lb_set_ep["LoadBalancedEndpointSetName"]).to be == "lb_set" - expect(lb_set_ep["LocalPort"]).to be == "80" - expect(lb_set_ep["Port"]).to be == "80" - expect(lb_set_ep["Protocol"]).to be == "TCP" - expect(lb_set_ep["LoadBalancerProbe"]["Port"]).to be == "80" - expect(lb_set_ep["LoadBalancerProbe"]["Protocol"]).to be == "TCP" - - lb_set2_ep = eps[2]["InputEndpoint"] - expect(lb_set2_ep["LoadBalancedEndpointSetName"]).to be == "lb_set_ssl" - expect(lb_set2_ep["LocalPort"]).to be == "443" - expect(lb_set2_ep["Port"]).to be == "443" - expect(lb_set2_ep["Protocol"]).to be == "TCP" - expect(lb_set2_ep["LoadBalancerProbe"]["Path"]).to be == "/healthcheck" - expect(lb_set2_ep["LoadBalancerProbe"]["Port"]).to be == "443" - expect(lb_set2_ep["LoadBalancerProbe"]["Protocol"]).to be == "HTTP" - end - - it "re-uses existing load balanced endpoints" do - @server_instance.config[:azure_dns_name] = "vmname" - @server_instance.config[:tcp_endpoints] = "443:443:EXTERNAL:lb_set2:/healthcheck" - expect(@server_instance).to receive(:is_image_windows?).at_least(:twice).and_return(false) - - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - endpoints = testxml.css("InputEndpoint") - - expect(endpoints.count).to be == 2 - - # Convert it to a hash as it's easier to test. - eps = [] - endpoints.each do |ep| - eps << Hash.from_xml(ep.to_s) - end - expect(eps[0]["InputEndpoint"]["Name"]).to be == "SSH" - - lb_set2_ep = eps[1]["InputEndpoint"] - expect(lb_set2_ep["LoadBalancedEndpointSetName"]).to be == "lb_set2" - expect(lb_set2_ep["LocalPort"]).to be == "443" - expect(lb_set2_ep["Port"]).to be == "443" - expect(lb_set2_ep["Protocol"]).to be == "tcp" - expect(lb_set2_ep["LoadBalancerProbe"]["Path"]).to be == "/healthcheck2" # The existing one wins. The 'healthcheck2' value is defined in the stub. - expect(lb_set2_ep["LoadBalancerProbe"]["Port"]).to be == "443" - expect(lb_set2_ep["LoadBalancerProbe"]["Protocol"]).to be == "http" - end - - it "allows internal load balancer to be specified" do - @server_instance.config[:azure_dns_name] = "vmname" - @server_instance.config[:tcp_endpoints] = "80:80:internal-lb-name:lb_set" # TODO: is this a good way of specifying this? - expect(@server_instance).to receive(:is_image_windows?).at_least(:twice).and_return(false) - - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - endpoints = testxml.css("InputEndpoint") - expect(endpoints.count).to be == 2 - - # Convert it to a hash as it's easier to test. - eps = [] - endpoints.each do |ep| - eps << Hash.from_xml(ep.to_s) - end - expect(eps[0]["InputEndpoint"]["Name"]).to be == "SSH" - - lb_set_ep = eps[1]["InputEndpoint"] - expect(lb_set_ep["LoadBalancedEndpointSetName"]).to be == "lb_set" - expect(lb_set_ep["LocalPort"]).to be == "80" - expect(lb_set_ep["Port"]).to be == "80" - expect(lb_set_ep["Protocol"]).to be == "TCP" - expect(lb_set_ep["LoadBalancerProbe"]["Port"]).to be == "80" - expect(lb_set_ep["LoadBalancerProbe"]["Protocol"]).to be == "TCP" - expect(lb_set_ep["LoadBalancerName"]).to be == "internal-lb-name" - end - - it "server create display server summary" do - @server_instance.config[:azure_dns_name] = "vmname" - expect(@server_instance).to receive(:msg_server_summary) - @server_instance.run - end - - context "Domain join:" do - before do - @server_instance.config[:azure_dns_name] = "vmname" - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:connection_protocol] = "winrm" - @server_instance.config[:connection_user] = "testuser" - @server_instance.config[:connection_password] = "connection_password" - end - - it "server create with domain join options" do - @server_instance.config[:azure_domain_name] = "testad.com" - @server_instance.config[:azure_domain_user] = "domainuser" - @server_instance.config[:azure_domain_passwd] = "domainuserpass" - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "DomainJoin Credentials Domain")).to eq("testad.com") - expect(xml_content(testxml, "DomainJoin Credentials Username")).to eq("domainuser") - expect(xml_content(testxml, "DomainJoin Credentials Password")).to eq("domainuserpass") - expect(xml_content(testxml, "JoinDomain")).to eq("testad.com") - end - - it "server create with domain join options in user principal name (UPN) format (user@fully-qualified-DNS-domain)" do - @server_instance.config[:azure_domain_user] = "domainuser@testad.com" - @server_instance.config[:azure_domain_passwd] = "domainuserpass" - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "DomainJoin Credentials Domain")).to eq("testad.com") - expect(xml_content(testxml, "DomainJoin Credentials Username")).to eq("domainuser") - expect(xml_content(testxml, "DomainJoin Credentials Password")).to eq("domainuserpass") - expect(xml_content(testxml, "JoinDomain")).to eq("testad.com") - end - - it 'server create with domain join options in fully-qualified-DNS-domain\\username format' do - @server_instance.config[:azure_domain_user] = 'testad.com\\domainuser' - @server_instance.config[:azure_domain_passwd] = "domainuserpass" - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "DomainJoin Credentials Domain")).to eq("testad.com") - expect(xml_content(testxml, "DomainJoin Credentials Username")).to eq("domainuser") - expect(xml_content(testxml, "DomainJoin Credentials Password")).to eq("domainuserpass") - expect(xml_content(testxml, "JoinDomain")).to eq("testad.com") - end - - it "server create with domain join options including name of the organizational unit (OU) in which the computer account is created" do - @server_instance.config[:azure_domain_user] = 'testad.com\\domainuser' - @server_instance.config[:azure_domain_passwd] = "domainuserpass" - @server_instance.config[:azure_domain_ou_dn] = "OU=HR,dc=opscode,dc=com" - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - expect(xml_content(testxml, "DomainJoin Credentials Domain")).to eq("testad.com") - expect(xml_content(testxml, "DomainJoin Credentials Username")).to eq("domainuser") - expect(xml_content(testxml, "DomainJoin Credentials Password")).to eq("domainuserpass") - expect(xml_content(testxml, "DomainJoin MachineObjectOU")).to eq("OU=HR,dc=opscode,dc=com") - expect(xml_content(testxml, "JoinDomain")).to eq("testad.com") - end - end - end - - context "missing create options" do - before do - @server_instance.config[:connection_protocol] = "winrm" - @server_instance.config[:connection_user] = "testuser" - @server_instance.config[:connection_password] = "connection_password" - @server_instance.config.delete(:azure_vm_name) - @server_instance.config.delete(:azure_storage_account) - end - - it "should error if domain user is not specified for domain join" do - @server_instance.config[:azure_dns_name] = "vmname" - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - - @server_instance.config[:azure_domain_name] = "testad.com" - @server_instance.config[:azure_domain_passwd] = "domainuserpass" - expect(@server_instance.ui).to receive(:error).with("Must specify both --azure-domain-user and --azure-domain-passwd.") - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "should error if password for domain user is not specified for domain join" do - @server_instance.config[:azure_dns_name] = "vmname" - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - - @server_instance.config[:azure_domain_name] = "testad.com" - @server_instance.config[:azure_domain_user] = "domainuser" - expect(@server_instance.ui).to receive(:error).with("Must specify both --azure-domain-user and --azure-domain-passwd.") - expect { @server_instance.run }.to raise_error(SystemExit) - end - end - - context "when --azure-dns-name is not specified" do - before(:each) do - @server_instance.config[:azure_dns_name] = nil - @server_instance.config[:azure_vm_name] = nil - end - - it "generate unique dns name" do - dns_name = [] - 5.times do - # send() to access private get_dns_name method of @server_instance - dns = @server_instance.send(:get_dns_name, @server_instance.config[:azure_dns_name]) - expect(dns_name).to_not include(dns) - dns_name.push(dns) - end - end - - it "include vmname in dnsname if --azure-vm-name specified" do - @server_instance.config[:azure_vm_name] = "vmname" - dns = @server_instance.send(:get_dns_name, @server_instance.config[:azure_dns_name]) - expect(dns).to include("vmname") - end - end - - context "#cleanup_and_exit" do - it "service leak cleanup" do - expect { @server_instance.service.cleanup_and_exit("hosted_srvc", "storage_srvc") }.to raise_error(NoMethodError) - end - - it "service leak cleanup with nil params" do - expect(@server_instance.service.connection.hosts).to_not receive(:delete) - expect(@server_instance.service.connection.storageaccounts).to_not receive(:delete) - expect { @server_instance.service.cleanup_and_exit(nil, nil) }.to raise_error(SystemExit) - end - - it "service leak cleanup with valid params" do - ret_val = Object.new - ret_val.define_singleton_method(:content) { "" } - expect(@server_instance.service.connection.hosts).to receive(:delete).with("hosted_srvc").and_return(ret_val) - expect(@server_instance.service.connection.storageaccounts).to receive(:delete).with("storage_srvc").and_return(ret_val) - expect { @server_instance.service.cleanup_and_exit("hosted_srvc", "storage_srvc") }.to raise_error(SystemExit) - end - - it "display proper warn messages on cleanup fails" do - ret_val = Object.new - ret_val.define_singleton_method(:content) { "ConflictError" } - ret_val.define_singleton_method(:text) { "ConflictError" } - expect(@server_instance.service.connection.hosts).to receive(:delete).with("hosted_srvc").and_return(ret_val) - expect(@server_instance.service.connection.storageaccounts).to receive(:delete).with("storage_srvc").and_return(ret_val) - expect { @server_instance.service.cleanup_and_exit("hosted_srvc", "storage_srvc") }.to raise_error(SystemExit) - end - end - - context "connect to existing DNS tests" do - before do - @server_instance.config[:azure_connect_to_existing_dns] = true - end - - it "should throw error when DNS does not exist" do - @server_instance.config[:azure_dns_name] = "does-not-exist" - @server_instance.config[:connection_user] = "azureuser" - @server_instance.config[:connection_password] = "Jetstream123!" - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "port should be connection-port value specified in the option" do - @server_instance.config[:connection_protocol] = "winrm" - @server_instance.config[:connection_user] = "testuser" - @server_instance.config[:connection_password] = "Jetstream123!" - @server_instance.config[:connection_port] = "5990" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - @server_params = @server_instance.create_server_def - expect(@server_params[:port]).to be == "5990" - end - - it "extract user name when --connection-user contains domain name" do - @server_instance.config[:connection_protocol] = "winrm" - @server_instance.config[:connection_user] = 'domain\\testuser' - @server_instance.config[:connection_password] = "Jetstream123!" - @server_instance.config[:connection_port] = "5990" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - @server_params = @server_instance.create_server_def - expect(@server_params[:connection_user]).to be == "testuser" - end - - it "port should be unique number when connection-port not specified for linux image" do - @server_instance.config[:connection_user] = "azureuser" - @server_instance.config[:connection_password] = "Jetstream123!" - @server_instance.config[:connection_protocol] = "ssh" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(false) - @server_params = @server_instance.create_server_def - expect(@server_params[:port]).to_not be == "22" - end - - it "port should be connection-port value specified in the option" do - @server_instance.config[:connection_protocol] = "ssh" - @server_instance.config[:connection_user] = "azureuser" - @server_instance.config[:connection_password] = "Jetstream123!" - @server_instance.config[:connection_port] = "24" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(false) - @server_params = @server_instance.create_server_def - expect(@server_params[:port]).to be == "24" - end - - it "port should be 22 if user specified --connection-port 22" do - @server_instance.config[:connection_protocol] = "ssh" - @server_instance.config[:connection_user] = "azureuser" - @server_instance.config[:connection_password] = "Jetstream123!" - @server_instance.config[:connection_port] = "22" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(false) - @server_params = @server_instance.create_server_def - expect(@server_params[:port]).to be == "22" - end - - it "port should be 5985 if user specified --connection-port 5985" do - @server_instance.config[:connection_user] = "azureuser" - @server_instance.config[:connection_password] = "Jetstream123!" - @server_instance.config[:connection_port] = "5985" - @server_instance.config[:connection_protocol] = "winrm" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - @server_params = @server_instance.create_server_def - expect(@server_params[:port]).to be == "5985" - end - end - end - - describe "for connection protocol winrm:" do - before do - @server_instance.config[:connection_protocol] = "winrm" - @server_instance.config[:connection_user] = "testuser" - @server_instance.config[:connection_password] = "connection_password" - allow(@server_instance.ui).to receive(:error) - allow(@server_instance).to receive(:msg_server_summary) - end - - it "check if all server params are set correctly" do - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - @server_params = @server_instance.create_server_def - expect(@server_params[:os_type]).to be == "Windows" - expect(@server_params[:admin_password]).to be == "connection_password" - expect(@server_params[:connection_protocol]).to be == "winrm" - expect(@server_params[:azure_dns_name]).to be == "service001" - expect(@server_params[:azure_vm_name]).to be == "vm002" - expect(@server_params[:connection_user]).to be == "testuser" - expect(@server_params[:port]).to be == "5985" - end - - it "--connection-user cannot be 'administrator'" do - expect(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:connection_user] = "administrator" - expect { @server_instance.create_server_def }.to raise_error(SystemExit) - end - - it "--connection-user cannot be 'admin*'" do - expect(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.config[:connection_user] = "Admin12" - expect { @server_instance.create_server_def }.to raise_error(SystemExit) - end - - context "bootstrap node" do - it "successful bootstrap of windows instance" do - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - expect(@server_instance).to receive(:wait_until_virtual_machine_ready).exactly(1).times.and_return(true) - @server_instance.run - end - - it "sets encrypted data bag secret parameter" do - @server_instance.config[:encrypted_data_bag_secret] = "test_encrypted_data_bag_secret" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - @server_instance.run - end - - it "sets encrypted data bag secret file parameter" do - @server_instance.config[:encrypted_data_bag_secret_file] = "test_encrypted_data_bag_secret_file" - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - @server_instance.run - end - - it "sets winrm authentication protocol for windows vm" do - @server_instance.config[:winrm_auth_method] = "negotiate" - expect(@server_instance).to receive(:is_image_windows?).at_least(:twice).and_return(true) - @server_instance.run - end - - it "sets 'msi_url' correctly" do - @server_instance.config[:msi_url] = "https://opscode-omnibus-packages.s3.amazonaws.com/windows/2008r2/x86_64/chef-client-12.3.0-1.msi" - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.run - end - - it "does not use 'install_as_service' anymore" do - @server_instance.config[:install_as_service] = true - allow(@server_instance).to receive(:is_image_windows?).and_return(true) - @server_instance.run - end - end - end - - describe "for connection protocol ssh:" do - before do - @server_instance.config[:connection_protocol] = "ssh" - allow(@server_instance).to receive(:msg_server_summary) - end - - context "linux instance" do - before do - @server_instance.config[:connection_user] = "connection_user" - @server_instance.config[:connection_password] = "connection_password" - end - - it "check if all server params are set correctly" do - expect(@server_instance).to receive(:is_image_windows?).exactly(3).and_return(false) - @server_params = @server_instance.create_server_def - expect(@server_params[:os_type]).to be == "Linux" - expect(@server_params[:connection_user]).to be == "connection_user" - expect(@server_params[:connection_password]).to be == "connection_password" - expect(@server_params[:connection_protocol]).to be == "ssh" - expect(@server_params[:azure_dns_name]).to be == "service001" - expect(@server_params[:azure_vm_name]).to be == "vm002" - expect(@server_params[:port]).to be == "22" - end - - context "ssh key" do - before do - @server_instance.config[:connection_password] = nil - @server_instance.config[:ssh_identity_file] = "path_to_rsa_private_key" - end - - it "check if ssh-key set correctly" do - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(false) - @server_params = @server_instance.create_server_def - expect(@server_params[:os_type]).to be == "Linux" - expect(@server_params[:ssh_identity_file]).to be == "path_to_rsa_private_key" - expect(@server_params[:connection_user]).to be == "connection_user" - expect(@server_params[:connection_protocol]).to be == "ssh" - expect(@server_params[:azure_dns_name]).to be == "service001" - end - - it "successful bootstrap with ssh key" do - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(false) - allow(@server_instance.service.connection.certificates).to receive(:generate_public_key_certificate_data).and_return("cert_data") - expect(@server_instance.service.connection.certificates).to receive(:create) - expect { @server_instance.run }.not_to raise_error - end - end - - context "bootstrap" do - before do - @server_params = @server_instance.create_server_def - end - - it "enables sudo password when connection_user is not root" do - expect { @server_instance.run }.not_to raise_error - end - - it "does not enable sudo password when connection_user is root" do - @server_instance.config[:connection_user] = "root" - expect { @server_instance.run }.not_to raise_error - end - - it "sets secret parameter" do - @server_instance.config[:encrypted_data_bag_secret] = "test_secret" - expect { @server_instance.run }.not_to raise_error - end - - it "sets secret file parameter" do - @server_instance.config[:encrypted_data_bag_secret_file] = "test_secret_file" - expect { @server_instance.run }.not_to raise_error - end - - it "sets first_boot_attributes to empty hash when json_attributes parameter not specified" do - expect { @server_instance.run }.not_to raise_error - end - - it "sets first_boot_attributes when json_attributes parameter specified" do - @server_instance.config[:json_attributes] = '{"keyattr":"value"}' - expect { @server_instance.run }.not_to raise_error - end - end - end - end - - describe "for connection protocol cloud-api:" do - before do - @server_instance.config[:connection_protocol] = "cloud-api" - allow(@server_instance).to receive(:msg_server_summary) - @server_instance.config[:run_list] = ["getting-started"] - @server_instance.config[:validation_client_name] = "testorg-validator" - @server_instance.config[:chef_server_url] = "https://api.opscode.com/organizations/testorg" - end - - after do - @server_instance.config.delete(:connection_protocol) - @server_instance.config.delete(:run_list) - @server_instance.config.delete(:validation_client_name) - @server_instance.config.delete(:chef_server_url) - end - - context "get_chef_extension_public_params" do - before do - @server_instance.config[:bootstrap_version] = "12.4.2" - @server_instance.config[:extended_logs] = true - @server_instance.config[:chef_daemon_interval] = "16" - end - - let(:public_config) do - { - client_rb: "chef_server_url \t \"https://localhost:443\"\nvalidation_client_name\t\"chef-validator\"", - runlist: '"getting-started"', - extendedLogs: "true", - custom_json_attr: {}, - chef_daemon_interval: "16", - bootstrap_options: { chef_server_url: "https://localhost:443", - validation_client_name: "chef-validator", - bootstrap_version: "12.4.2" }, - } - end - - it "should set public config properly" do - expect(@server_instance).to receive(:get_chef_extension_name) - expect(@server_instance).to receive(:get_chef_extension_publisher) - expect(@server_instance).to receive(:get_chef_extension_version) - expect(@server_instance).to receive(:get_chef_extension_private_params) - response = @server_instance.create_server_def - expect(response[:chef_extension_public_param]).to be == public_config - end - - it "should add daemon in public config if daemon options is given" do - @server_instance.config[:daemon] = "service" - public_config[:daemon] = "service" - expect(@server_instance).to receive(:get_chef_extension_name) - expect(@server_instance).to receive(:get_chef_extension_publisher) - expect(@server_instance).to receive(:get_chef_extension_version) - expect(@server_instance).to receive(:get_chef_extension_private_params) - response = @server_instance.create_server_def - expect(response[:chef_extension_public_param]).to be == public_config - end - end - - context "get azure chef extension version" do - it "take user specified version" do - @server_instance.config[:azure_chef_extension_version] = "1200.12" - expect(@server_instance.get_chef_extension_version).to eq("1200.12") - end - end - - context "windows instance:" do - it "successful create" do - expect(@server_instance).to_not receive(:bootstrap_exec) - expect(@server_instance).to receive(:get_chef_extension_version) - expect(@server_instance).to receive(:get_chef_extension_public_params) - expect(@server_instance).to receive(:get_chef_extension_private_params) - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(true) - expect(@server_instance).to receive(:wait_until_virtual_machine_ready).exactly(1).times.and_return(true) - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - testxml.css('InputEndpoint Protocol:contains("TCP")').each do |port| - expect(port.parent.css("LocalPort").text).to_not eq("5985") - end - end - - it "check if all server params are set correctly" do - version = Nokogiri::XML::Builder.new do |xml| - xml.ResourceExtensionReferences do - xml.Version "11.12" - end - end - expect(@server_instance).to_not receive(:bootstrap_exec) - - allow(@server_instance.service.connection).to receive(:query_azure).and_return(version.doc) - expect(@server_instance).to receive(:get_chef_extension_public_params) - expect(@server_instance).to receive(:get_chef_extension_private_params) - expect(@server_instance).to receive(:is_image_windows?).exactly(4).times.and_return(true) - server_config = @server_instance.create_server_def - expect(server_config[:chef_extension]).to eq("ChefClient") - expect(server_config[:chef_extension_publisher]).to eq("Chef.Bootstrap.WindowsAzure") - expect(server_config[:chef_extension_version]).to eq("11.*") - expect(server_config).to include(:chef_extension_public_param) - expect(server_config).to include(:chef_extension_private_param) - end - end - - context "linux instance" do - it "successful create" do - expect(@server_instance).to_not receive(:bootstrap_exec) - expect(@server_instance).to receive(:get_chef_extension_version) - expect(@server_instance).to receive(:get_chef_extension_public_params) - expect(@server_instance).to receive(:get_chef_extension_private_params) - expect(@server_instance).to receive(:is_image_windows?).exactly(3).times.and_return(false) - expect(@server_instance).to receive(:wait_until_virtual_machine_ready).exactly(1).times.and_return(true) - @server_instance.run - testxml = Nokogiri::XML(@receivedXML) - testxml.css('InputEndpoint Protocol:contains("TCP")').each do |port| - expect(port.parent.css("LocalPort").text).to eq("22") - end - end - - it "check if all server params are set correctly" do - version = Nokogiri::XML::Builder.new do |xml| - xml.ResourceExtensionReferences do - xml.Version "11.12" - end - end - expect(@server_instance).to_not receive(:bootstrap_exec) - allow(@server_instance.service.connection).to receive(:query_azure).and_return(version.doc) - expect(@server_instance).to receive(:get_chef_extension_private_params) - expect(@server_instance).to receive(:is_image_windows?).exactly(4).times.and_return(false) - server_config = @server_instance.create_server_def - expect(server_config[:chef_extension]).to eq("LinuxChefClient") - expect(server_config[:chef_extension_publisher]).to eq("Chef.Bootstrap.WindowsAzure") - expect(server_config[:chef_extension_version]).to eq("11.*") - expect(server_config).to include(:chef_extension_public_param) - expect(server_config[:chef_extension_public_param][:runlist]).to eq(@server_instance.config[:run_list].first.to_json) - expect(server_config).to include(:chef_extension_private_param) - end - end - - shared_context "private config contents" do - before do - allow(File).to receive(:read).and_return("my_client_pem") - end - - it "uses Chef ClientBuilder to generate client_pem and sets private config properly" do - expect_any_instance_of(Chef::Knife::Bootstrap::ClientBuilder).to receive(:run) - expect_any_instance_of(Chef::Knife::Bootstrap::ClientBuilder).to receive(:client_path).and_return(File.dirname(__FILE__) + "/assets/client.pem") - response = @server_instance.get_chef_extension_private_params - expect(response).to be == private_config - end - end - - context "when validation key is not present", :chef_gte_12_only do - context "when encrypted_data_bag_secret option is passed" do - let(:private_config) do - { client_pem: "my_client_pem", - encrypted_data_bag_secret: "my_encrypted_data_bag_secret" } - end - - before do - @server_instance.config[:encrypted_data_bag_secret] = "my_encrypted_data_bag_secret" - end - - include_context "private config contents" - end - - context "when encrypted_data_bag_secret_file option is passed" do - let(:private_config) do - { client_pem: "my_client_pem", - encrypted_data_bag_secret: "PgIxStCmMDsuIw3ygRhmdMtStpc9EMiWisQXoP" } - end - - before do - @server_instance.config[:encrypted_data_bag_secret_file] = File.dirname(__FILE__) + "/assets/secret_file" - end - - include_context "private config contents" - end - end - - context "when SSL certificate file option is passed but file does not exist physically" do - before do - allow_any_instance_of(Chef::Knife::Bootstrap::ClientBuilder).to receive(:run) - allow_any_instance_of(Chef::Knife::Bootstrap::ClientBuilder).to receive(:client_path).and_return(File.dirname(__FILE__) + "/assets/client.pem") - @server_instance.config[:cert_path] = "~/my_cert.crt" - end - - it "raises an error and exits" do - expect(@server_instance.ui).to receive(:error).with("Specified SSL certificate does not exist.") - expect { @server_instance.get_chef_extension_private_params }.to raise_error(SystemExit) - end - end - end - - describe "extended_logs feature for cloud-api connection protocol" do - describe "run" do - before do - @server_instance.config[:connection_password] = "connection_password" - allow(Chef::Log).to receive(:info) - allow(@server_instance).to receive(:validate_asm_keys!) - allow(@server_instance).to receive(:validate_params!) - allow(@server_instance).to receive(:get_dns_name) - allow(@server_instance.service).to receive(:create_server) - allow(@server_instance).to receive(:create_server_def) - allow(@server_instance).to receive(:wait_until_virtual_machine_ready) - allow(@server_instance.service).to receive(:get_role_server) - allow(@server_instance).to receive(:msg_server_summary) - allow(@server_instance).to receive(:plugin_create_instance!) - end - - context "connection_protocol is not cloud-api and extended_logs is false" do - before do - @server_instance.config[:connection_protocol] = "winrm" - @server_instance.config[:extended_logs] = false - end - - it "does not invoke fetch_chef_client_logs method" do - expect(@server_instance).to_not receive(:fetch_chef_client_logs) - expect { @server_instance.run }.not_to raise_error - end - end - - context "connection_protocol is cloud-api" do - before do - @server_instance.config[:connection_protocol] = "cloud-api" - end - - context "extended_logs is false" do - before do - @server_instance.config[:extended_logs] = false - end - - it "does not invoke fetch_chef_client_logs method" do - expect(@server_instance).to_not receive(:fetch_chef_client_logs) - @server_instance.run - end - end - - context "extended_logs is true" do - before do - @server_instance.config[:extended_logs] = true - end - - it "invoke fetch_chef_client_logs method" do - expect(@server_instance).to receive(:fetch_chef_client_logs) - @server_instance.run - end - end - end - end - - describe "fetch_chef_client_logs" do - context "role not found" do - before do - allow(@server_instance).to receive( - :fetch_role - ).and_return(nil) - end - - it "displays role not found error" do - expect(@server_instance.ui).to receive(:error).with( - "chef-client run logs could not be fetched since role vm002 could not be found." - ) - @server_instance.fetch_chef_client_logs(nil, nil) - end - end - - context "extension not found" do - before do - allow(@server_instance).to receive( - :fetch_role - ).and_return("vm002") - allow(@server_instance).to receive( - :fetch_extension - ).and_return(nil) - end - - it "displays extension not found error" do - expect(@server_instance.ui).to receive(:error).with( - "Unable to find Chef extension under role vm002." - ) - @server_instance.fetch_chef_client_logs(nil, nil) - end - end - - context "substatus not found in server role response" do - before do - allow(@server_instance).to receive( - :fetch_role - ).and_return("vm002") - allow(@server_instance).to receive( - :fetch_extension - ).and_return("extension") - allow(@server_instance).to receive( - :fetch_substatus - ).and_return(nil) - @start_time = Time.now - end - - context "wait time has not exceeded wait timeout limit" do - it "displays wait messages and re-invokes fetch_chef_client_logs method recursively" do - @server_instance.instance_eval do - class << self - alias fetch_chef_client_logs_mocked fetch_chef_client_logs - end - end - expect(@server_instance).to receive(:print).exactly(1).times - expect(@server_instance).to receive(:sleep).with(30) - expect(@server_instance).to receive( - :fetch_chef_client_logs - ).with(@start_time, 30) - @server_instance.fetch_chef_client_logs_mocked(@start_time, 30) - end - end - - context "wait time has exceeded wait timeout limit" do - it "displays wait timeout exceeded message" do - expect(@server_instance.ui).to receive(:error).with( - "\nchef-client run logs could not be fetched since fetch process exceeded wait timeout of -1 minutes.\n" - ) - @server_instance.fetch_chef_client_logs(@start_time, -1) - end - end - end - - context "substatus found in server role response" do - before do - @server_instance.config[:azure_vm_name] = "vm04" - allow(@server_instance.service).to receive( - :deployment_name - ).and_return("deploymentExtension") - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@server_instance.service).to receive( - :deployment - ).and_return(deployment) - end - - it "displays chef-client run logs and exit status to the user" do - expect(@server_instance).to receive( - :puts - ).exactly(4).times - expect(@server_instance).to receive(:print) - @server_instance.fetch_chef_client_logs(@start_time, 30) - end - end - end - - describe "fetch_role" do - context "role not found" do - before do - @server_instance.config[:azure_vm_name] = "vm09" - end - - it "returns nil" do - response = fetch_role_from_xml - expect(response).to be nil - end - end - - context "role found" do - before do - @server_instance.config[:azure_vm_name] = "vm01" - end - - it "returns the role" do - response = fetch_role_from_xml - expect(response).to_not be nil - expect(response.at_css("RoleName").text).to eq "vm01" - end - end - end - - describe "fetch_extension" do - context "Chef Extension not found" do - context "other extension(s) available" do - context "example-1" do - before do - @server_instance.config[:azure_vm_name] = "vm01" - @role = fetch_role_from_xml - end - - it "returns nil" do - response = @server_instance.fetch_extension(@role) - expect(response).to be nil - end - end - - context "example-2" do - before do - @server_instance.config[:azure_vm_name] = "vm07" - @role = fetch_role_from_xml - end - - it "returns nil" do - response = @server_instance.fetch_extension(@role) - expect(response).to be nil - end - end - end - - context "none of the extension available" do - before do - @server_instance.config[:azure_vm_name] = "vm06" - @role = fetch_role_from_xml - end - - it "returns nil" do - response = @server_instance.fetch_extension(@role) - expect(response).to be nil - end - end - end - - context "Chef Extension found" do - context "for Windows platform" do - before do - @server_instance.config[:azure_vm_name] = "vm02" - @role = fetch_role_from_xml - end - - it "returns the Windows Chef Extension" do - response = @server_instance.fetch_extension(@role) - expect(response).to_not be nil - expect(response.at_css("HandlerName").text).to eq \ - "Chef.Bootstrap.WindowsAzure.ChefClient" - end - end - - context "for Linux platform" do - before do - @server_instance.config[:azure_vm_name] = "vm03" - @role = fetch_role_from_xml - end - - it "returns the Linux Chef Extension" do - response = @server_instance.fetch_extension(@role) - expect(response).to_not be nil - expect(response.at_css("HandlerName").text).to eq \ - "Chef.Bootstrap.WindowsAzure.LinuxChefClient" - end - end - end - end - - describe "fetch_substatus" do - context "substatus list not found" do - before do - @server_instance.config[:azure_vm_name] = "vm02" - role = fetch_role_from_xml - @extension = @server_instance.fetch_extension(role) - end - - it "returns nil" do - response = @server_instance.fetch_substatus(@extension) - expect(response).to be nil - end - end - - context "substatus list found" do - context "but it does not contain chef-client run logs substatus" do - before do - @server_instance.config[:azure_vm_name] = "vm03" - role = fetch_role_from_xml - @extension = @server_instance.fetch_extension(role) - end - - it "returns nil" do - response = @server_instance.fetch_substatus(@extension) - expect(response).to be nil - end - end - - context "and it do contain chef-client run logs substatus" do - before do - @server_instance.config[:azure_vm_name] = "vm04" - role = fetch_role_from_xml - @extension = @server_instance.fetch_extension(role) - end - - it "returns the substatus" do - response = @server_instance.fetch_substatus(@extension) - expect(response).to_not be nil - expect(response.at_css("Name").text).to eq "Chef Client run logs" - expect(response.at_css("Status").text).to eq "Success" - expect(response.at_css("Message").text).to eq "MyChefClientRunLogs" - end - end - end - end - end - - def fetch_role_from_xml - allow(@server_instance.service).to receive( - :deployment_name - ).and_return("deploymentExtension") - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@server_instance.service).to receive( - :deployment - ).and_return(deployment) - @server_instance.fetch_role - end -end diff --git a/spec/unit/azure_server_delete_spec.rb b/spec/unit/azure_server_delete_spec.rb deleted file mode 100644 index f8fc7cd9..00000000 --- a/spec/unit/azure_server_delete_spec.rb +++ /dev/null @@ -1,256 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "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: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - name: "vm01", - azure_service_location: "West Europe", - azure_source_image: "azure_source_image", - azure_vm_size: "Small", - azure_dns_name: "service001", - azure_storage_account: "ka001testeurope", - }.each do |key, value| - @server_instance.config[key] = value - end - @connection = @server_instance.service.connection - stub_query_azure(@connection) - - # allow(@server_instance).to receive(:confirm).and_return(:true) - allow(@server_instance.service.ui).to receive(:confirm).and_return(true) - allow(@server_instance).to receive(:puts) - allow(@server_instance).to receive(:print) - allow(@server_instance.ui).to receive(:warn) - expect(@server_instance.ui).to_not receive(:error).and_call_original - end - - it "delete server" do - @server_instance.name_args = ["role001"] - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - @server_instance.run - end - - it "wait for server delete" do - @server_instance.config[:wait] = true - @server_instance.name_args = ["role001"] - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - expect(@connection).to receive(:query_azure).with("hostedservices/service001/deployments/deployment001/roles/role001", "delete") - # comp=media deletes associated vhd - expect(@connection).to receive(:query_azure).with("disks/deployment001-role002-0-201241722728", "delete", "", "comp=media", true) - - @server_instance.run - end - - it "wait for server delete and preserve_azure_vhd" do - @server_instance.config[:wait] = true - @server_instance.config[:preserve_azure_vhd] = true - @server_instance.name_args = ["role001"] - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - expect(@connection).to receive(:query_azure).with("hostedservices/service001/deployments/deployment001/roles/role001", "delete") - expect(@connection).to receive(:query_azure).with("disks/deployment001-role002-0-201241722728", "get") - # absent comp=media param preserve vhd disk and delete os disk - expect(@connection).to receive(:query_azure).with("disks/deployment001-role002-0-201241722728", "delete") - @server_instance.run - end - - it "delete everything if cloud service contains only one role and no wait and no preserve option set" do - @server_instance.config[:wait] = false - @server_instance.config[:azure_dns_name] = "service002" - @server_instance.name_args = ["vm01"] - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - # comp=media deletes cloud service, role and associated disks - expect(@connection).to receive(:query_azure).with("hostedservices/service002", "delete", "", "comp=media", false) - @server_instance.run - end - - it "delete everything if cloud service contains only one role and preserve-azure-dns true set and no wait and no other preserve option set" do - @server_instance.config[:wait] = false - @server_instance.config[:azure_dns_name] = "service002" - @server_instance.config[:preserve_azure_dns_name] = true - @server_instance.name_args = ["vm01"] - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - # comp=media deletes role and associated disks - expect(@connection).to receive(:query_azure).with("hostedservices/service002/deployments/testrequest", "delete", "", "comp=media", false) - @server_instance.run - end - - it "display valid nomenclature in delete output" do - @server_instance.name_args = ["role001"] - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:msg_pair).with(@server_instance.service.ui, "DNS Name", @server_instance.config[:azure_dns_name] + ".cloudapp.net") - expect(@server_instance.service).to receive(:msg_pair).with(@server_instance.service.ui, "VM Name", "role001") - expect(@server_instance.service).to receive(:msg_pair).with(@server_instance.service.ui, "Size", "Small") - expect(@server_instance.service).to receive(:msg_pair).with(@server_instance.service.ui, "Public Ip Address", "65.52.249.191") - expect(@server_instance.service).to receive(:delete_server).and_call_original - # expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - @server_instance.run - end - - it "test hosted service cleanup with shared service" do - @server_instance.name_args = ["role001"] - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - expect(@connection.hosts).to_not receive(:delete) - @server_instance.run - end - - it "don't cleanup hosted service when --preserve-azure-dns-name param set" do - @server_instance.name_args = ["role001"] - @server_instance.config[:preserve_azure_dns_name] = true - expect(@server_instance.ui).to receive(:warn).twice - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - expect(@connection.hosts).to_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] - - @server_instance.config[:azure_dns_name] = "service001" - @server_instance.config[:preserve_azure_os_disk] = true - expect(@server_instance.service).to receive(:delete_server).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - # test correct params are passed to azure API. - expect(@connection).to receive(:query_azure).with("hostedservices/#{@server_instance.config[:azure_dns_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 = %w{vm002 role002 role001} - @server_instance.name_args = test_hostnames - - @server_instance.config[:azure_dns_name] = "service001" - @server_instance.config[:preserve_azure_os_disk] = true - - expect(@server_instance.service).to receive(:delete_server).exactly(3).times.and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(12).times - - # test correct calls are made to azure API. - expect(@connection).to receive(:query_azure).with("hostedservices/#{@server_instance.config[:azure_dns_name]}/deployments/deployment001", "delete") - expect(@connection).to receive(:query_azure).with("hostedservices/#{@server_instance.config[:azure_dns_name]}/deployments/deployment001/roles/#{test_hostnames[1]}", "delete") - expect(@connection).to receive(:query_azure).with("hostedservices/#{@server_instance.config[:azure_dns_name]}/deployments/deployment001/roles/#{test_hostnames[0]}", "delete") - - @server_instance.run - end - - it "should preserve OS Disk when --preserve-azure-os-disk is set." do - test_hostname = "role002" - test_diskname = "disk1" - @server_instance.name_args = [test_hostname] - @server_instance.config[:preserve_azure_os_disk] = true - expect(@server_instance.service).to receive(:delete_server).exactly(:once).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - expect(@connection).to receive(:query_azure).with("hostedservices/#{@server_instance.config[:azure_dns_name]}/deployments/deployment001/roles/#{test_hostname}", "delete").exactly(:once) - expect(@connection).to_not receive(:query_azure).with("disks/#{test_diskname}", "delete") - @server_instance.run - end - - it "should delete OS Disk and VHD when --wait set and --preserve-azure-os-disk, --preserve-azure-vhd are not set." do - @server_instance.config[:wait] = true - test_hostname = "role001" - test_diskname = "deployment001-role002-0-201241722728" - @server_instance.name_args = [test_hostname] - expect(@server_instance.service).to receive(:delete_server).exactly(1).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - expect(@connection).to receive(:query_azure).with("disks/#{test_diskname}", "delete", "", "comp=media", true) - @server_instance.run - end - - it "should preserve VHD when --preserve-azure-vhd is set." do - test_hostname = "role001" - test_diskname = "deployment001-role002-0-201241722728" - @server_instance.config[:preserve_azure_vhd] = true - @server_instance.name_args = [test_hostname] - expect(@server_instance.service).to receive(:delete_server).exactly(1).and_call_original - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - expect(@connection).to receive(:query_azure).with("disks/#{test_diskname}", "delete") - @server_instance.run - end - - describe "Storage Account" do - before(:each) do - test_hostname = "role001" - @test_storage_account = "auxpreview104imagestore" - @server_instance.name_args = [test_hostname] - end - - it "should be deleted when --delete-azure-storage-account is set." do - @server_instance.config[:delete_azure_storage_account] = true - expect(@connection).to receive(:query_azure).with("storageservices/#{@test_storage_account}", "delete") - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - @server_instance.run - end - - it "should not be deleted when --delete-azure-storage-account is not set." do - expect(@connection).to_not receive(:query_azure).with("storageservices/#{@test_storage_account}", "delete") - expect(@server_instance.service).to receive(:msg_pair).exactly(4).times - - @server_instance.run - end - end - - it "should give a warning and exit when both --preserve-azure-os-disk and --delete-azure-storage-account are set." do - test_hostname = "role001" - @server_instance.config[:preserve_azure_os_disk] = true - @server_instance.config[:delete_azure_storage_account] = true - @server_instance.name_args = [test_hostname] - test_storage_account = "auxpreview104imagestore" - test_diskname = "deployment001-role002-0-201241722728" - expect(@connection).to_not receive(:query_azure).with("disks/#{test_diskname}", "delete") - expect(@connection).to_not receive(:query_azure).with("storageservices/#{test_storage_account}", "delete") - expect(@server_instance.ui).to receive(:warn).with("Cannot delete storage account while keeping OS Disk. Please set any one option.") - expect(lambda { @server_instance.validate_disk_and_storage }).to raise_error(SystemExit) - end - - after(:each) do - @server_instance.config[:preserve_azure_os_disk] = false if @server_instance.config[:preserve_azure_os_disk] # cleanup config for each run - @server_instance.config[:delete_azure_storage_account] = false if @server_instance.config[:delete_azure_storage_account] - end -end diff --git a/spec/unit/azure_server_list_spec.rb b/spec/unit/azure_server_list_spec.rb deleted file mode 100644 index b756ece4..00000000 --- a/spec/unit/azure_server_list_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureServerList do - include AzureSpecHelper - include QueryAzureMock - - before do - @server_management_instance = Azure::ServiceManagement::ASMInterface.new - @server_instance = create_instance(Chef::Knife::AzureServerList) - end - - it "should display DNS Name, VM Name, Status, IP Address, SSH Port, Winrm Port and RDP Port for ASM command." do - stub_query_azure(@server_instance.service.connection) - allow(@server_instance).to receive(:puts) - expect(@server_instance.ui).to receive(:list) - .with(["DNS Name", "VM Name", "Status", "IP Address", "SSH Port", "WinRM Port", "RDP Port", - "service001.cloudapp.net", "vm002", "ready", "65.52.251.57", "22", "", "", - "service001.cloudapp.net", "role002", "ready", "65.52.249.191", "23", "", "", - "service001.cloudapp.net", "role001", "ready", "65.52.249.191", "22", "", "", - "service002.cloudapp.net", "vm01", "ready", "65.52.251.144", "54047", "", "", - "service004.cloudapp.net", "ssh-vm", "ready", "65.52.251.57", "22", "", "", - "service004.cloudapp.net", "winrm-vm", "ready", "65.52.249.191", "", "5985", "3389", - "vmname.cloudapp.net", "vmname", "ready", "65.52.251.57", "22", "", ""], :uneven_columns_across, 7) - @server_instance.run - end - - it "should return public port for Remote Desktop if set" do - arr_hash = [{ "Name" => "PowerShell", "Vip" => "13.92.236.37", "PublicPort" => "5986", "LocalPort" => "5986" }, - { "Name" => "Remote Desktop", "Vip" => "13.92.236.37", "PublicPort" => "3389", "LocalPort" => "3389" }] - rdp_port = @server_management_instance.rdp_port(arr_hash) - expect(rdp_port).to be == "3389" - end - - it "should return empty port for Remote Desktop if not set" do - arr_hash = [{ "Name" => "PowerShell", "Vip" => "13.92.236.37", "PublicPort" => "5986", "LocalPort" => "5986" }] - rdp_port = @server_management_instance.rdp_port(arr_hash) - expect(rdp_port).to be == "" - end -end diff --git a/spec/unit/azure_server_show_spec.rb b/spec/unit/azure_server_show_spec.rb deleted file mode 100644 index eae58b43..00000000 --- a/spec/unit/azure_server_show_spec.rb +++ /dev/null @@ -1,140 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureServerShow do - include AzureSpecHelper - include QueryAzureMock - before do - @server_instance = create_instance(Chef::Knife::AzureServerShow) - - stub_query_azure(@server_instance.service.connection) - allow(@server_instance).to receive(:puts) - end - - it "should display server information." do - @server_instance.name_args = %w{role206 role001 role002 vm002 vm01 ssh-vm - winrm-vm vmname} - expect(@server_instance.ui).to receive(:list).with( - ["Role name", "role001", - "Status", "ReadyRole", - "Size", "Small", - "Hosted service name", "service001", - "Deployment name", "deployment001", - "Host name", "role001", - "SSH port", "22", - "Public IP", "65.52.249.191", - "Thumbprint", "4BAE99A617B7B4F975C51A572CB8420F66477F4C"], - :columns_across, - 2 - ) - expect(@server_instance.ui).to receive(:list).with( - ["Role name", "role002", - "Status", "RoleStateUnknown", - "Size", "Small", - "Hosted service name", "service001", - "Deployment name", "deployment001", - "Host name", "role002", - "SSH port", "23", - "Public IP", "65.52.249.191", - "Thumbprint", "4BAE99A617B7B4F975C51A572CB8420F66477F4C"], - :columns_across, - 2 - ) - expect(@server_instance.ui).to receive(:list).with( - ["Role name", "vm002", - "Status", "ReadyRole", - "Size", "ExtraSmall", - "Hosted service name", "service001", - "Deployment name", "deployment001", - "Host name", "myVm2", - "SSH port", "22", - "Public IP", "65.52.251.57", - "Thumbprint", "4BAE99A617B7B4F975C51A572CB8420F66477F4C"], - :columns_across, - 2 - ) - expect(@server_instance.ui).to receive(:list).with( - ["Ports open", "Local port", "IP", "Public port", - "tcp", "66", "65.52.251.57", "66"], - :columns_across, - 4 - ).exactly(3).times - expect(@server_instance.ui).to receive(:list).with( - ["Ports open", "Local port", "IP", "Public port", - "tcp", "3389", "65.52.249.191", "3389"], - :columns_across, - 4 - ).exactly(1).times - expect(@server_instance.ui).to receive(:list).with( - ["Role name", "vm01", - "Status", "ReadyRole", - "Size", "ExtraSmall", - "Hosted service name", "service002", - "Deployment name", "testrequest", - "Host name", "myVm", - "SSH port", "54047", - "Public IP", "65.52.251.144", - "Thumbprint", "4BAE99A617B7B4F975C51A572CB8420F66477F4C"], - :columns_across, - 2 - ) - expect(@server_instance.ui).to receive(:list).with( - ["Role name", "ssh-vm", - "Status", "ReadyRole", - "Size", "ExtraSmall", - "Hosted service name", "service004", - "Deployment name", "deployment004", - "Host name", "ssh-vm", - "SSH port", "22", - "Public IP", "65.52.251.57", - "Thumbprint", "4BAE99A617B7B4F975C51A572CB8420F66477F4C"], - :columns_across, - 2 - ) - expect(@server_instance.ui).to receive(:list).with( - ["Role name", "winrm-vm", - "Status", "ReadyRole", - "Size", "Small", - "Hosted service name", "service004", - "Deployment name", "deployment004", - "Host name", "winrm-vm", - "WinRM port", "5985", - "Public IP", "65.52.249.191", - "Thumbprint", "4BAE99A617B7B4F975C51A572CB8420F66477F4C"], - :columns_across, - 2 - ) - expect(@server_instance.ui).to receive(:list).with( - ["Role name", "vmname", - "Status", "ReadyRole", - "Size", "ExtraSmall", - "Hosted service name", "vmname", - "Deployment name", "deployment001", - "Host name", "myVm2", - "SSH port", "22", - "Public IP", "65.52.251.57", - "Thumbprint", "4BAE99A617B7B4F975C51A572CB8420F66477F4C"], - :columns_across, - 2 - ) - @server_instance.run - end - -end diff --git a/spec/unit/azure_vnet_create_spec.rb b/spec/unit/azure_vnet_create_spec.rb deleted file mode 100644 index 9b7703af..00000000 --- a/spec/unit/azure_vnet_create_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureVnetCreate do - include AzureSpecHelper - include QueryAzureMock - - before do - @server_instance = create_instance(Chef::Knife::AzureVnetCreate) - @connection = @server_instance.service.connection - stub_query_azure(@connection) - allow(@server_instance).to receive(:puts) - allow(@server_instance).to receive(:print) - end - - it "should fail missing args." do - expect(@connection.vnets).to_not receive(:create) - expect(@server_instance.ui).to receive(:error).exactly(3).times - expect { @server_instance.run }.to raise_error(SystemExit) - end - - it "should succeed." do - @server_instance.config[:azure_network_name] = "new-net" - @server_instance.config[:azure_affinity_group] = "ag" - @server_instance.config[:azure_address_space] = "10.0.0.0/24" - @server_instance.config[:azure_subnet_name] = "Subnet-7" - expect(@connection.vnets).to receive(:create).with( - azure_vnet_name: "new-net", - azure_ag_name: "ag", - azure_address_space: "10.0.0.0/24", - azure_subnet_name: "Subnet-7" - ).and_call_original - expect(@server_instance.ui).to_not receive(:warn) - expect(@server_instance.ui).to_not receive(:error) - @server_instance.run - end -end diff --git a/spec/unit/azure_vnet_list_spec.rb b/spec/unit/azure_vnet_list_spec.rb deleted file mode 100644 index 001ea9f7..00000000 --- a/spec/unit/azure_vnet_list_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe Chef::Knife::AzureAgList do - include AzureSpecHelper - include QueryAzureMock - before do - @server_instance = create_instance(Chef::Knife::AzureVnetList) - stub_query_azure(@server_instance.service.connection) - allow(@server_instance).to receive(:puts) - end - - it "should display Name, Affinity Group, and State columns." do - expect(@server_instance.ui).to receive(:list).with( - ["Name", "Affinity Group", "State", - "jm-vnet-test", "jm-affinity-group", "Created", - "vnet-test-2", "test", "Created", - "vnname", "agname", "Created"], - :uneven_columns_across, - 3 - ) - @server_instance.run - end -end diff --git a/spec/unit/azurerm_base_spec.rb b/spec/unit/azurerm_base_spec.rb index 2461ee7e..4f0ba987 100644 --- a/spec/unit/azurerm_base_spec.rb +++ b/spec/unit/azurerm_base_spec.rb @@ -101,7 +101,7 @@ def validate_cert describe "Token related test cases" do context "Xplat Azure login validation" do context "Platform is Linux" do - let (:azure_prefix) { @dummy.instance_variable_get(:@azure_prefix) } + let(:azure_prefix) { @dummy.instance_variable_get(:@azure_prefix) } before(:each) do allow(Chef::Platform).to receive(:windows?).and_return(false) end diff --git a/spec/unit/azurerm_server_create_spec.rb b/spec/unit/azurerm_server_create_spec.rb index dfead327..80f471cc 100644 --- a/spec/unit/azurerm_server_create_spec.rb +++ b/spec/unit/azurerm_server_create_spec.rb @@ -24,7 +24,6 @@ describe Chef::Knife::AzurermServerCreate do include AzureSpecHelper include QueryAzureMock - include AzureUtility before do @arm_server_instance = create_arm_instance(Chef::Knife::AzurermServerCreate) diff --git a/spec/unit/azurerm_server_delete_spec.rb b/spec/unit/azurerm_server_delete_spec.rb index 53851fa0..ab2f78ef 100644 --- a/spec/unit/azurerm_server_delete_spec.rb +++ b/spec/unit/azurerm_server_delete_spec.rb @@ -25,7 +25,7 @@ describe "delete server without deleting resource group" do before do @arm_server_instance = create_arm_instance(Chef::Knife::AzurermServerDelete) - allow(@arm_server_instance.service.ui).to receive(:confirm).and_return (true) + allow(@arm_server_instance.service.ui).to receive(:confirm).and_return(true) @compute_client = double("ComputeManagementClient") @service = @arm_server_instance.service @@ -85,7 +85,7 @@ describe "delete respective resource group along with server" do before do @arm_server_instance = create_arm_instance(Chef::Knife::AzurermServerDelete) - allow(@arm_server_instance.service.ui).to receive(:confirm).and_return (true) + allow(@arm_server_instance.service.ui).to receive(:confirm).and_return(true) allow_any_instance_of(Chef::Knife::AzurermBase).to receive(:get_azure_cli_version).and_return("1.0.0") @resource_client = double("ResourceManagementClient") @service = @arm_server_instance.service @@ -99,7 +99,7 @@ server = double("server") allow(server).to receive(:nil?).and_return("false") @arm_server_instance.config[:delete_resource_group] = true - allow(@arm_server_instance.service.ui).to receive(:confirm).and_return (true) + allow(@arm_server_instance.service.ui).to receive(:confirm).and_return(true) expect(@arm_server_instance).to receive(:validate_arm_keys!).with(:azure_resource_group_name) expect(@arm_server_instance.service).to receive(:resource_management_client).and_return(@resource_client) diff --git a/spec/unit/bootstrap_azure_spec.rb b/spec/unit/bootstrap_azure_spec.rb deleted file mode 100644 index 8d186b5b..00000000 --- a/spec/unit/bootstrap_azure_spec.rb +++ /dev/null @@ -1,791 +0,0 @@ -# -# Author:: Aliasgar Batterywala () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" -require_relative "../../lib/azure/resource_management/ARM_interface" -require "chef/knife/bootstrap" - -describe Chef::Knife::BootstrapAzure do - include AzureSpecHelper - include QueryAzureMock - include AzureUtility - - before do - @bootstrap_azure_instance = create_instance(Chef::Knife::BootstrapAzure) - @service = @bootstrap_azure_instance.service - @bootstrap_azure_instance.config[:azure_dns_name] = "test-dns-01" - @bootstrap_azure_instance.name_args = ["test-vm-01"] - @server_role = Azure::Role.new("connection") - allow(@bootstrap_azure_instance.ui).to receive(:info) - allow(@bootstrap_azure_instance).to receive(:puts) - allow(@bootstrap_azure_instance).to receive(:check_license) - end - - describe "parameters validation" do - it "raises error when azure_subscription_id is not specified" do - @bootstrap_azure_instance.config.delete(:azure_subscription_id) - expect(@bootstrap_azure_instance.ui).to receive(:error) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - it "raises error when azure_mgmt_cert is not specified" do - @bootstrap_azure_instance.config.delete(:azure_mgmt_cert) - expect(@bootstrap_azure_instance.ui).to receive(:error) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - it "raises error when azure_api_host_name is not specified" do - @bootstrap_azure_instance.config.delete(:azure_api_host_name) - expect(@bootstrap_azure_instance.ui).to receive(:error) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - it "raises error when server name is not specified" do - @bootstrap_azure_instance.name_args = [] - expect(@service).to_not receive(:add_extension) - expect(@bootstrap_azure_instance.ui).to receive( - :error - ).with("Please specify the SERVER name which needs to be bootstrapped via the Chef Extension.") - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - it "raises error when more than one server name is specified" do - @bootstrap_azure_instance.name_args = %w{test-vm-01 test-vm-02 test-vm-03} - expect(@bootstrap_azure_instance.name_args.length).to be == 3 - expect(@service).to_not receive(:add_extension) - expect(@bootstrap_azure_instance.ui).to receive( - :error - ).with("Please specify only one SERVER name which needs to be bootstrapped via the Chef Extension.") - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - it "raises error when server name specified does not exist under the given hosted service" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@service).to_not receive(:add_extension) - expect(@service).to receive( - :find_server - ).and_return([]) - expect(@bootstrap_azure_instance.ui).to receive( - :error - ).with("Server test-vm-01 does not exist under the hosted service test-dns-01.") - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - it "raises error when hosted service specified does not exist" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@service).to_not receive(:add_extension) - expect(@service).to receive( - :find_server - ).and_return(nil) - expect(@bootstrap_azure_instance.ui).to receive( - :error - ).with("Hosted service test-dns-01 does not exist.") - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - it "raises error when hosted service name is not given but invalid server name is given" do - @bootstrap_azure_instance.config.delete(:azure_dns_name) - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@service).to_not receive(:add_extension) - expect(@service).to receive( - :find_server - ).and_return(nil) - expect(@bootstrap_azure_instance.ui).to receive( - :error - ).with("Server test-vm-01 does not exist.") - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - - context "server name specified do exist" do - context "hosted service name is specified in @bootstrap_azure_instance.config object" do - before do - @server_role.hostedservicename = "my_new_dns" - allow(@server_role).to receive_message_chain( - :os_type, :downcase - ).and_return("windows") - allow(@server_role).to receive( - :deployname - ).and_return("") - allow(@server_role).to receive(:role_xml).and_return("") - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_version - ) - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_public_params - ) - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_private_params - ) - end - - it "does not raise error when server name do exist and does not re-initializes azure_dns_name in bootstrap_azure_instance's config using server object" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@service).to receive(:add_extension) - expect(@service).to receive( - :find_server - ).and_return(@server_role) - expect { @bootstrap_azure_instance.run }.not_to raise_error - expect(@bootstrap_azure_instance.config[:azure_dns_name]).to be == "test-dns-01" - end - end - - context "hosted service name is not specified in @bootstrap_azure_instance.config object or anywhere else" do - before do - @bootstrap_azure_instance.config.delete(:azure_dns_name) - @server_role.hostedservicename = "my_new_dns" - allow(@server_role).to receive_message_chain( - :os_type, :downcase - ).and_return("windows") - allow(@server_role).to receive( - :deployname - ).and_return("") - allow(@server_role).to receive(:role_xml).and_return("") - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_version - ) - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_public_params - ) - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_private_params - ) - end - - it "does not raise error when server name do exist and initializes azure_dns_name in bootstrap_azure_instance's config using server object" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@service).to receive(:add_extension) - expect(@service).to receive( - :find_server - ).and_return(@server_role) - expect { @bootstrap_azure_instance.run }.not_to raise_error - expect(@bootstrap_azure_instance.config[:azure_dns_name]).to be == "my_new_dns" - end - end - end - end - - describe "extended_logs functionality" do - context "when extended_logs is false" do - it "deploys the Chef Extension on the server but then does not wait and fetch the chef-client run logs" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@bootstrap_azure_instance).to receive(:set_ext_params) - expect(@service).to receive(:add_extension) - expect(@bootstrap_azure_instance).to_not receive(:print) - expect(@bootstrap_azure_instance).to_not receive(:wait_until_extension_available) - expect(@bootstrap_azure_instance).to_not receive(:fetch_chef_client_logs) - @bootstrap_azure_instance.run - end - end - - context "when extended_logs is true" do - before do - @bootstrap_azure_instance.config[:extended_logs] = true - end - - it "deploys the Chef Extension on the server and also waits and fetch the chef-client run logs" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@bootstrap_azure_instance).to receive(:set_ext_params) - expect(@service).to receive(:add_extension) - expect(@bootstrap_azure_instance).to receive(:print).exactly(2).times - expect(@bootstrap_azure_instance).to receive(:wait_until_extension_available) - expect(@bootstrap_azure_instance).to receive(:fetch_chef_client_logs) - @bootstrap_azure_instance.run - end - - context "when Chef Extension becomes available/ready within the prescribed timeout" do - it "successfully deploys the Chef Extension on the server and also successfully fetches the chef-client run logs without raising any error" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@bootstrap_azure_instance).to receive(:set_ext_params) - expect(@service).to receive(:add_extension) - expect(@bootstrap_azure_instance).to receive(:print).exactly(2).times - expect(@bootstrap_azure_instance).to receive(:wait_until_extension_available) - expect(@bootstrap_azure_instance).to receive(:fetch_chef_client_logs) - expect { @bootstrap_azure_instance.run }.to_not raise_error - end - end - - context "when Chef Extension does not become available/ready within the prescribed timeout" do - it "successfully deploys the Chef Extension on the server but fails to fetch the chef-client run logs as extension is unavailable and so it raises error and exits" do - expect(@bootstrap_azure_instance.name_args.length).to be == 1 - expect(@bootstrap_azure_instance).to receive(:set_ext_params) - expect(@service).to receive(:add_extension) - expect(@bootstrap_azure_instance).to receive(:print).exactly(1).times - allow(@bootstrap_azure_instance).to receive( - :wait_until_extension_available - ).and_raise( - "\nUnable to fetch chef-client run logs as Chef Extension seems to be unavailable even after 11 minutes of its deployment.\n" - ) - expect(@bootstrap_azure_instance).to_not receive(:fetch_chef_client_logs) - expect(@bootstrap_azure_instance.ui).to receive(:error).with( - "\nUnable to fetch chef-client run logs as Chef Extension seems to be unavailable even after 11 minutes of its deployment.\n" - ) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - end - end - end - - describe "os_type and os_version support validation" do - context "invalid os_type for the given server" do - before do - allow(@server_role).to receive( - :os_type - ).and_return("Abc") - end - - it "raises an error" do - expect(@service).to receive( - :find_server - ).and_return(@server_role) - expect(@bootstrap_azure_instance.ui).to receive( - :error - ).with("OS type Abc is not supported.") - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - end - - context "invalid os_version for the given Linux server" do - before do - allow(@server_role).to receive( - :os_type - ).and_return("Linux") - allow(@server_role).to receive( - :os_version - ).and_return("Suse") - end - - it "raises an error" do - expect(@service).to receive( - :find_server - ).and_return(@server_role) - expect(@bootstrap_azure_instance.ui).to receive( - :error - ).with("OS version Suse for OS type Linux is not supported.") - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @bootstrap_azure_instance.run }.to raise_error(SystemExit) - end - end - - context "valid os_type and valid os_version" do - before do - allow(@server_role).to receive( - :deployname - ).and_return("test-deploy-01") - allow(@server_role).to receive( - :role_xml - ).and_return("vm-role-xml") - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_version - ).and_return("1210.*") - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_public_params - ).and_return( - "chef_ext_public_params" - ) - allow(@bootstrap_azure_instance).to receive( - :get_chef_extension_private_params - ).and_return( - "chef_ext_private_params" - ) - end - - context "for Linux" do - before do - allow(@server_role).to receive( - :os_type - ).and_return("Linux") - allow(@server_role).to receive( - :os_version - ).and_return("CentOS") - end - - it "sets the extension parameters for Linux platform" do - expect(@service).to receive( - :find_server - ).and_return(@server_role) - response = @bootstrap_azure_instance.set_ext_params - expect(response[:chef_extension]).to be == "LinuxChefClient" - expect(response[:azure_dns_name]).to be == "test-dns-01" - expect(response[:deploy_name]).to be == "test-deploy-01" - expect(response[:role_xml]).to be == "vm-role-xml" - expect(response[:azure_vm_name]).to be == "test-vm-01" - expect(response[:chef_extension_publisher]).to be == "Chef.Bootstrap.WindowsAzure" - expect(response[:chef_extension_version]).to be == "1210.*" - expect(response[:chef_extension_public_param]).to be == "chef_ext_public_params" - expect(response[:chef_extension_private_param]).to be == "chef_ext_private_params" - end - end - - context "for Windows" do - before do - allow(@server_role).to receive( - :os_type - ).and_return("Windows") - end - - it "sets the extension parameters for Windows platform" do - expect(@service).to receive( - :find_server - ).and_return(@server_role) - response = @bootstrap_azure_instance.set_ext_params - expect(response[:chef_extension]).to be == "ChefClient" - expect(response[:azure_dns_name]).to be == "test-dns-01" - expect(response[:deploy_name]).to be == "test-deploy-01" - expect(response[:role_xml]).to be == "vm-role-xml" - expect(response[:azure_vm_name]).to be == "test-vm-01" - expect(response[:chef_extension_publisher]).to be == "Chef.Bootstrap.WindowsAzure" - expect(response[:chef_extension_version]).to be == "1210.*" - expect(response[:chef_extension_public_param]).to be == "chef_ext_public_params" - expect(response[:chef_extension_private_param]).to be == "chef_ext_private_params" - end - end - end - end - - describe "parse role list xml" do - it "reads os_type and os_version from role list 1 xml" do - role_list_1_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/parse_role_list_xml/role_list_1.xml")) - role = Azure::Role.new("connection") - role.parse_role_list_xml(role_list_1_xml) - expect(role.role_xml).to be == role_list_1_xml - expect(role.os_type).to be == "Linux" - expect(role.os_version).to be == "842c8b9c6cvxzcvxzcv048xvbvge2323qe4c3__OpenLogic-CentOS-67-20140205" - end - - it "reads os_type and os_version from role list 2 xml" do - role_list_2_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/parse_role_list_xml/role_list_2.xml")) - role = Azure::Role.new("connection") - role.parse_role_list_xml(role_list_2_xml) - expect(role.role_xml).to be == role_list_2_xml - expect(role.os_type).to be == "Windows" - expect(role.os_version).to be == "a6dfsdfwerfdfc0bc8f24rwefsd4ds01__Windows-Server-2012-R2-20141128-en.us-127GB.vhd" - end - end - - describe "add_extension" do - it "calls role update and prints success message on successful completion" do - expect(@service.ui).to receive(:info).with( - "Started with Chef Extension deployment on the server test-vm-01..." - ) - expect(@service).to receive_message_chain( - :connection, :roles, :update - ) - expect(@service.ui).to receive(:info).with( - "\nSuccessfully deployed Chef Extension on the server test-vm-01." - ) - @service.add_extension(@bootstrap_azure_instance.name_args[0]) - end - - it "calls role update and raises error on unsuccessful completion" do - expect(@service).to receive_message_chain( - :connection, :roles, :update - ).and_raise - expect(Chef::Log).to receive(:error) - expect(Chef::Log).to receive(:debug).at_least(:once) - @service.add_extension(@bootstrap_azure_instance.name_args[0]) - end - end - - describe "roles_update" do - before do - @roles = Azure::Roles.new("connection") - @role = double("Role") - allow(Azure::Role).to receive(:new).and_return(@role) - end - - it "calls setup_extension and update methods of Role class" do - expect(@role).to receive( - :setup_extension - ).with({}).and_return(nil) - expect(@role).to receive(:update).with( - @bootstrap_azure_instance.name_args[0], {}, nil - ) - @roles.update(@bootstrap_azure_instance.name_args[0], {}) - end - end - - describe "setup_extension" do - before do - @role = Azure::Role.new("connection") - updated_role_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/setup_extension/updated_role.xml")) - allow(@role).to receive(:update_role_xml_for_extension).and_return(updated_role_xml) - @update_role_xml_for_extension = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/setup_extension/update_role.xml")) - allow(@role).to receive(:puts) - end - - it "creates new xml for update role" do - response = @role.setup_extension({}) - expect(response).to eq(@update_role_xml_for_extension.to_xml) - end - end - - describe "update_role_xml_for_extension" do - before do - @params = { - chef_extension_publisher: "Chef.Bootstrap.WindowsAzure", - chef_extension_version: "1210.12", - chef_extension_public_param: "MyPublicParamsValue", - chef_extension_private_param: "MyPrivateParamsValue", - azure_dns_name: @bootstrap_azure_instance.config[:azure_dns_name], - } - @role = Azure::Role.new("connection") - end - - context "ResourceExtensionReferences node is not present in role xml" do - before do - @input_role_1_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/update_role_xml_for_extension/input_role_1.xml")) - @output_role_1_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/update_role_xml_for_extension/output_role_1.xml")) - @params[:chef_extension] = "LinuxChefClient" - end - - it "adds ResourceExtensionReferences node with ChefExtension config" do - response = @role.update_role_xml_for_extension(@input_role_1_xml.at_css("Role"), @params) - expect(response.to_xml).to eq(@output_role_1_xml.at_css("Role").to_xml) - end - end - - context "ResourceExtensionReferences node is present in xml but it is empty" do - before do - @input_role_2_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/update_role_xml_for_extension/input_role_2.xml")) - @output_role_2_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/update_role_xml_for_extension/output_role_2.xml")) - @params[:chef_extension] = "ChefClient" - end - - it "updates ResourceExtensionReferences node with ChefExtension config" do - response = @role.update_role_xml_for_extension(@input_role_2_xml.at_css("Role"), @params) - expect(response.to_xml).to eq(@output_role_2_xml.at_css("Role").to_xml) - end - end - - context "ResourceExtensionReferences node is present in role xml but ChefExtension is not installed on the server" do - before do - @input_role_3_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/update_role_xml_for_extension/input_role_3.xml")) - @output_role_3_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/update_role_xml_for_extension/output_role_3.xml")) - @params[:chef_extension] = "ChefClient" - end - - it "adds ChefExtension config in ResourceExtensionReferences node" do - response = @role.update_role_xml_for_extension(@input_role_3_xml.at_css("Role"), @params) - expect(response.to_xml).to eq(@output_role_3_xml.at_css("Role").to_xml) - end - end - - context "ResourceExtensionReferences node is present in role xml and ChefExtension is already installed on the server" do - before do - @input_role_4_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/update_role_xml_for_extension/input_role_4.xml")) - @params[:chef_extension] = "LinuxChefClient" - @params[:azure_vm_name] = "test-vm-01" - end - - it "raises an error with message as 'ChefExtension is already installed on the server'" do - expect { @role.update_role_xml_for_extension(@input_role_4_xml.at_css("Role"), @params) }.to raise_error("Chef Extension is already installed on the server test-vm-01.") - end - end - end - - describe "role_update" do - before do - @role = Azure::Role.new("connection") - @role.connection = double("Connection") - allow(@role).to receive(:puts) - end - - it "does not raise error on update role success" do - expect(@role.connection).to receive(:query_azure) - expect(@role).to receive(:error_from_response_xml).and_return(["", ""]) - expect(Chef::Log).to_not receive(:debug) - expect { @role.update(@bootstrap_azure_instance.name_args[0], {}, "") }.not_to raise_error - end - - it "raises an error on update role failure" do - expect(@role.connection).to receive(:query_azure) - expect(@role).to receive(:error_from_response_xml) - .and_return(["InvalidXmlRequest", "The request body's XML was invalid or not correctly specified."]) - expect(Chef::Log).to receive(:debug).at_least(:once) - expect { @role.update(@bootstrap_azure_instance.name_args[0], {}, "") }.to raise_error("Unable to update role:InvalidXmlRequest : The request body's XML was invalid or not correctly specified.") - end - end - - describe "get_chef_extension_version" do - before do - allow(@service).to receive(:instance_of?).with( - Azure::ResourceManagement::ARMInterface - ).and_return(false) - allow(@service).to receive(:instance_of?).with( - Azure::ServiceManagement::ASMInterface - ).and_return(true) - end - - context "when extension version is set in knife.rb" do - before do - @bootstrap_azure_instance.config[:azure_chef_extension_version] = "1012.10" - end - - it "will pick up the extension version from knife.rb" do - response = @bootstrap_azure_instance.get_chef_extension_version("MyChefClient") - expect(response).to be == "1012.10" - end - end - - context "when extension version is not set in knife.rb" do - before do - @bootstrap_azure_instance.config.delete(:azure_chef_extension_version) - extensions_list_xml = Nokogiri::XML(readFile("bootstrap_azure_role_xmls/extensions_list.xml")) - allow(@service).to receive( - :get_extension - ).and_return(extensions_list_xml) - end - - it "will pick up the latest version of the extension" do - expect(@service).to_not receive(:get_latest_chef_extension_version) - response = @bootstrap_azure_instance.get_chef_extension_version("MyChefClient") - expect(response).to be == "1210.*" - end - end - end - - describe "wait_until_extension_available" do - context "extension_availability_wait_time has exceeded the extension_availability_wait_timeout" do - before do - @start_time = Time.now - end - - it "raises error saying unable to fetch chef-client run logs" do - expect { @bootstrap_azure_instance.wait_until_extension_available(@start_time, -1) }.to raise_error( - "\nUnable to fetch chef-client run logs as Chef Extension seems to be unavailable even after -1 minutes of its deployment.\n" - ) - end - end - - context "extension_availability_wait_time has not exceeded the extension_availability_wait_timeout" do - context "deployment not available" do - before do - @start_time = Time.now - deployment = Nokogiri::XML("") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "goes to sleep and then re-invokes the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to receive(:print).exactly(1).times - expect(@bootstrap_azure_instance).to receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - - context "deployment available" do - context "given role not available" do - before do - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "goes to sleep and then re-invokes the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to receive(:print).exactly(1).times - expect(@bootstrap_azure_instance).to receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - - context "given role available" do - context "GuestAgent not ready" do - before do - @bootstrap_azure_instance.name_args = ["vm05"] - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "goes to sleep and then re-invokes the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to receive(:print).exactly(1).times - expect(@bootstrap_azure_instance).to receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - - context "GuestAgent ready" do - context "none of the extension status available" do - before do - @bootstrap_azure_instance.name_args = ["vm06"] - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "goes to sleep and then re-invokes the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to receive(:print).exactly(1).times - expect(@bootstrap_azure_instance).to receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - - context "extension status(es) available apart from extension status for Chef Extension" do - context "example-1" do - before do - @bootstrap_azure_instance.name_args = ["vm01"] - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "goes to sleep and then re-invokes the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to receive(:print).exactly(1).times - expect(@bootstrap_azure_instance).to receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - - context "example-2" do - before do - @bootstrap_azure_instance.name_args = ["vm07"] - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "goes to sleep and then re-invokes the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to receive(:print).exactly(1).times - expect(@bootstrap_azure_instance).to receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - end - - context "extension status(es) available including extension status for Chef Extension" do - context "example-1" do - before do - @bootstrap_azure_instance.name_args = ["vm02"] - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "does not go to sleep and does not re-invoke the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to_not receive(:print) - expect(@bootstrap_azure_instance).to_not receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to_not receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - - context "example-2" do - before do - @bootstrap_azure_instance.name_args = ["vm03"] - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "does not go to sleep and does not re-invoke the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to_not receive(:print) - expect(@bootstrap_azure_instance).to_not receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to_not receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - - context "example-3" do - before do - @bootstrap_azure_instance.name_args = ["vm08"] - @start_time = Time.now - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance).to receive(:fetch_deployment).and_return(deployment) - end - - it "does not go to sleep and does not re-invoke the wait_until_extension_available method recursively" do - mock_recursive_call - expect(@bootstrap_azure_instance).to_not receive(:print) - expect(@bootstrap_azure_instance).to_not receive(:sleep).with(30) - expect(@bootstrap_azure_instance).to_not receive( - :wait_until_extension_available - ).with(@start_time, 10) - @bootstrap_azure_instance.wait_until_extension_available_mocked(@start_time, 10) - end - end - end - end - end - end - end - end - - describe "fetch_deployment" do - before do - allow(@bootstrap_azure_instance.service).to receive( - :deployment_name - ).and_return("deploymentExtension") - deployment = Nokogiri::XML readFile("extension_deployment_xml.xml") - allow(@bootstrap_azure_instance.service).to receive( - :deployment - ).and_return(deployment) - end - - it "returns the deployment" do - response = @bootstrap_azure_instance.fetch_deployment - expect(response).to_not be nil - expect(response.at_css("Deployment Name").text).to be == "deploymentExtension" - expect(response.css("RoleInstanceList RoleInstance RoleName").class).to be == Nokogiri::XML::NodeSet - expect(response.css("RoleInstanceList RoleInstance RoleName").children.count).to be == 8 - end - end - - def mock_recursive_call - @bootstrap_azure_instance.instance_eval do - class << self - alias wait_until_extension_available_mocked wait_until_extension_available - end - end - end -end diff --git a/spec/unit/bootstrap_azurerm_spec.rb b/spec/unit/bootstrap_azurerm_spec.rb index 078b6329..6f91e7a3 100644 --- a/spec/unit/bootstrap_azurerm_spec.rb +++ b/spec/unit/bootstrap_azurerm_spec.rb @@ -23,7 +23,6 @@ describe Chef::Knife::BootstrapAzurerm do include AzureSpecHelper include QueryAzureMock - include AzureUtility before do @bootstrap_azurerm_instance = create_arm_instance(Chef::Knife::BootstrapAzurerm) diff --git a/spec/unit/certificate_spec.rb b/spec/unit/certificate_spec.rb deleted file mode 100644 index cb87f3ae..00000000 --- a/spec/unit/certificate_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -describe "certificates" do - include AzureSpecHelper - include QueryAzureMock - - before do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - stub_query_azure(@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - describe "get_certificate" do - it "gets certificates for given dns name and fingerprint" do - certificate = @connection.certificates.get_certificate("unknown_yet", "7dbcac68f670a27cf5d9a4e6c4a8d097bff645e2") - expect(certificate).not_to be_empty - end - - it "it returns empty array if certificate not found" do - certificate = @connection.certificates.get_certificate("unknown_yet", "9dbcac68f670a27cf5d9a4e6c4a8d097bff645e2") - expect(certificate).to be_empty - end - end -end diff --git a/spec/unit/deploys_list_spec.rb b/spec/unit/deploys_list_spec.rb deleted file mode 100755 index d2fe017d..00000000 --- a/spec/unit/deploys_list_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -describe "deploys" do - include AzureSpecHelper - include QueryAzureMock - before "setup connection" do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure (@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - specify { expect(@connection.deploys.all.length).to be > 0 } - it "each deployment should have values" do - @connection.deploys.all.each do |deploy| - expect(deploy.name).to_not be nil - expect(deploy.status).to_not be nil - expect(deploy.url).to_not be nil - expect(deploy.roles.length).to be > 0 - end - end - it "each role should have values" do - @connection.deploys.all.each do |deploy| - # describe_deploy deploy - deploy.roles.each do |role| - # describe_role role - expect(role.name).to_not be nil - expect(role.status).to_not be nil - expect(role.size).to_not be nil - expect(role.ipaddress).to_not be nil - # We either have ssh port or winrm port on a role - expect(role.winrmport).to_not be nil if role.sshport.nil? - expect(role.sshport).to_not be nil if role.winrmport.nil? - expect(role.publicipaddress).to_not be nil - end - end - end - def describe_deploy(deploy) - puts "=============================" - puts "deployed service: " + deploy.hostedservicename + " deployment: " + deploy.name - end - - def describe_role(role) - puts "role: " + role.name - puts "status: " + role.status - puts "size: " + role.size - puts "ip address: " + role.ipaddress - puts "ssh port: " + role.sshport - puts "ssh ip address: " + role.publicipaddress - role.tcpports.each do |port| - puts " tcp: " + port["Name"] + " " + port["Vip"] + " " + - port["PublicPort"] + " " + port["LocalPort"] - end - role.udpports.each do |port| - puts " udp: " + port["Name"] + " " + port["Vip"] + " " + - port["PublicPort"] + " " + port["LocalPort"] - end - puts "=============================" - end -end diff --git a/spec/unit/disks_spec.rb b/spec/unit/disks_spec.rb deleted file mode 100755 index 93aeb413..00000000 --- a/spec/unit/disks_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -describe "disks" do - include AzureSpecHelper - include QueryAzureMock - before "setup connection" do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure (@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - context "mock with actually retrieved values" do - it "should find strings" do - items = @connection.disks.all - expect(items.length).to be > 1 - items.each do |disk| - expect(disk.name).to_not be nil - end - end - it "should contain an attached disk" do - items = @connection.disks.all - count = 0 - items.each do |item| - if item.attached == true - count += 1 - end - end - expect(count).to be == 1 - end - it "should contain unattached disks" do - items = @connection.disks.all - count = 0 - items.each do |item| - if item.attached == false - count += 1 - end - end - expect(count).to be == 7 - end - it "should clear all unattached disks" do - @connection.disks.clear_unattached - expect(@deletecount).to be == 7 - end - end -end diff --git a/spec/unit/hosts_spec.rb b/spec/unit/hosts_spec.rb deleted file mode 100755 index 881aec43..00000000 --- a/spec/unit/hosts_spec.rb +++ /dev/null @@ -1,90 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -describe "hosts" do - include AzureSpecHelper - include QueryAzureMock - before "setup connection" do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure (@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - context "get all hosts" do - specify { expect(@connection.hosts.all.length).to be > 1 } - it "entry fields should not be nil" do - items = @connection.hosts.all - items.each do |host| - expect(host.name).to_not be nil - expect(host.url).to_not be nil - expect(host.label).to_not be nil - expect(host.dateCreated).to_not be nil - expect(host.description).to_not be nil - expect(host.location).to_not be nil - expect(host.dateModified).to_not be nil - expect(host.status).to_not be nil - end - end - specify { expect(@connection.hosts.exists?("notExpectedName")).to be == false } - specify { expect(@connection.hosts.exists?("service001")).to be == true } - end - - context "create a new host with service location" do - it "using explicit parameters it should pass in expected body" do - params = { :azure_dns_name => "service003", :azure_service_location => "West US", "hosted_azure_service_location" => "Windows Azure Preview" } - host = @connection.hosts.create(params) - expect(@postname).to be == "hostedservices" - expect(@postverb).to be == "post" - expect(@postbody).to eq(readFile("create_host_location.xml")) - end - it "using default parameters it should pass in expected body" do - params = { azure_dns_name: "service003", azure_service_location: "West US" } - host = @connection.hosts.create(params) - expect(@postname).to be == "hostedservices" - expect(@postverb).to be == "post" - expect(@postbody).to eq(readFile("create_host_location.xml")) - end - end - context "create a new host with affinity group" do - it "using explicit parameters it should pass in expected body" do - params = { azure_dns_name: "service003", azure_affinity_group: "test-affinity" } - host = @connection.hosts.create(params) - expect(@postname).to be == "hostedservices" - expect(@postverb).to be == "post" - expect(@postbody).to eq(readFile("create_host_affinity.xml")) - end - end - context "delete a host" do - it "should pass in correct name, verb, and body" do - @connection.hosts.delete("service001") - expect(@deletename).to be == "hostedservices/service001" - expect(@deleteverb).to be == "delete" - expect(@deletebody).to be nil - end - end -end diff --git a/spec/unit/images_spec.rb b/spec/unit/images_spec.rb deleted file mode 100755 index 270033ba..00000000 --- a/spec/unit/images_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -describe "images" do - include AzureSpecHelper - include QueryAzureMock - before "setup connection" do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure(@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - context "mock with actually retrieved values" do - it "should find strings" do - items = @connection.images.all - expect(items.length).to be > 1 - items.each do |image| - expect(image.category).to_not be nil - expect(image.label).to_not be nil - expect(image.name).to_not be nil - expect(image.os).to_not be nil - expect(image.eula).to_not be nil - expect(image.description).to_not be nil - end - end - it "should contain a linux image" do - items = @connection.images.all - foundLinux = false - items.each do |item| - if item.os == "Linux" - foundLinux = true - end - end - expect(foundLinux).to be true - end - end -end diff --git a/spec/unit/query_azure_mock.rb b/spec/unit/query_azure_mock.rb old mode 100755 new mode 100644 index 866dd721..eba0c952 --- a/spec/unit/query_azure_mock.rb +++ b/spec/unit/query_azure_mock.rb @@ -16,14 +16,8 @@ # require_relative "../spec_helper" -require_relative "../../lib/azure/service_management/ASM_interface" module QueryAzureMock - include AzureUtility - def create_service - @service = Azure::ServiceManagement::ASMInterface.new(TEST_PARAMS) - end - def create_instance(object) @server_instance = object.new @server_instance.merge_configs diff --git a/spec/unit/roles_create_spec.rb b/spec/unit/roles_create_spec.rb deleted file mode 100755 index 2bd20ded..00000000 --- a/spec/unit/roles_create_spec.rb +++ /dev/null @@ -1,217 +0,0 @@ -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -module Azure - class Certificate - class Random - def self.rand(_var) - 1 - end - end - class Time - def self.now - 1 - end - end - end -end - -describe "roles" do - include AzureSpecHelper - include QueryAzureMock - before do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure @server_instance.service.connection - @connection = @server_instance.service.connection - end - - 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(name: "vm002", preserve_azure_os_disk: true) - expect(@deletename).to be == "hostedservices/service001/deployments/deployment001/roles/vm002" - expect(@deleteverb).to be == "delete" - expect(@deletebody).to be nil - end - end - end - - 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(name: "vm01", preserve_azure_os_disk: true) - expect(@deletename).to be == "hostedservices/service002/deployments/testrequest" - expect(@deleteverb).to be == "delete" - expect(@deletebody).to be nil - end - end - end - - context "create a new role" do - it "should pass in expected body" do - params = { - azure_dns_name: "service001", - azure_api_host_name: "management.core.windows.net", - azure_vm_name: "vm01", - connection_user: "jetstream", - connection_password: "jetstream1!", - media_location_prefix: "auxpreview104", - azure_os_disk_name: "disk004Test", - azure_source_image: "SUSE__OpenSUSE64121-03192012-en-us-15GB", - azure_vm_size: "ExtraSmall", - tcp_endpoints: "80:80, 3389:3389, 993:993, 44: 45", - udp_endpoints: "65:65,75", - azure_storage_account: "storageaccount001", - connection_protocol: "ssh", - os_type: "Linux", - port: "22", - - } - - deploy = @connection.deploys.create(params) - expect(readFile("create_role.xml")).to eq(@receivedXML) - end - end - - describe "assign tcp endpoint name" do - before do - @params = { - azure_dns_name: "service001", - azure_api_host_name: "management.core.windows.net", - azure_vm_name: "vm01", - connection_user: "jetstream", - connection_password: "jetstream1!", - media_location_prefix: "auxpreview104", - azure_source_image: "SUSE__OpenSUSE64121-03192012-en-us-15GB", - azure_vm_size: "ExtraSmall", - azure_storage_account: "storageaccount001", - connection_protocol: "ssh", - } - end - - context "tcp_endpoint 80:80" do - it "assigns tcp endpoint name HTTP" do - @params[:tcp_endpoints] = "80:80" - @connection.deploys.create(@params) - doc = Nokogiri::XML::Document.parse(@receivedXML) - doc.remove_namespaces! - endpoints = doc.xpath("//InputEndpoints/InputEndpoint") - endpoints[1].children.each do |node| - expect(node.children.text).to eq("HTTP") if node.name == "Name" - end - end - end - - context "tcp_endpoint 44:45" do - it "generates tcp endpoint name as TCPEndpoint_chef_" do - @params[:tcp_endpoints] = "44:45" - @connection.deploys.create(@params) - doc = Nokogiri::XML::Document.parse(@receivedXML) - doc.remove_namespaces! - endpoints = doc.xpath("//InputEndpoints/InputEndpoint") - endpoints[1].children.each do |node| - expect(node.children.text).to eq("TCPEndpoint_chef_44") if node.name == "Name" - end - end - end - end - - context "create a new deployment" do - it "should pass in expected body" do - params = { - azure_dns_name: "unknown_yet", - azure_api_host_name: "management.core.windows.net", - azure_vm_name: "vm01", - connection_user: "jetstream", - connection_password: "jetstream1!", - media_location_prefix: "auxpreview104", - azure_os_disk_name: "disk004Test", - azure_source_image: "SUSE__OpenSUSE64121-03192012-en-us-15GB", - azure_vm_size: "ExtraSmall", - azure_storage_account: "storageaccount001", - connection_protocol: "ssh", - os_type: "Linux", - port: "22", - } - - deploy = @connection.deploys.create(params) - expect(readFile("create_deployment.xml")).to eq(@receivedXML) - end - it "create request with virtual network" do - params = { - azure_dns_name: "unknown_yet", - azure_api_host_name: "management.core.windows.net", - azure_vm_name: "vm01", - connection_user: "jetstream", - connection_password: "jetstream1!", - media_location_prefix: "auxpreview104", - azure_os_disk_name: "disk004Test", - azure_source_image: "SUSE__OpenSUSE64121-03192012-en-us-15GB", - azure_vm_size: "ExtraSmall", - azure_storage_account: "storageaccount001", - connection_protocol: "ssh", - os_type: "Linux", - port: "22", - azure_network_name: "test-network", - azure_subnet_name: "test-subnet", - } - - deploy = @connection.deploys.create(params) - expect(readFile("create_deployment_virtual_network.xml")).to eq(@receivedXML) - end - - it "with ssh key" do - params = { - azure_dns_name: "unknown_yet", - azure_api_host_name: "management.core.windows.net", - azure_vm_name: "vm01", - connection_user: "jetstream", - ssh_identity_file: File.dirname(__FILE__) + "/assets/key_rsa", - media_location_prefix: "auxpreview104", - azure_os_disk_name: "disk004Test", - azure_source_image: "SUSE__OpenSUSE64121-03192012-en-us-15GB", - azure_vm_size: "ExtraSmall", - azure_storage_account: "storageaccount001", - connection_protocol: "ssh", - os_type: "Linux", - port: "22", - } - - deploy = @connection.deploys.create(params) - expect(readFile("create_deployment_key.xml")).to eq(@receivedXML) - end - - describe "WinRM bootstrapping" do - it "customizes the WinRM config" do - params = { - azure_dns_name: "unknown_yet", - azure_vm_name: "vm01", - azure_api_host_name: "management.core.windows.net", - connection_user: "build", - admin_password: "foobar", - ssl_cert_fingerprint: "7FCCD713CC390E3488290BF7A106AD267B5AC2A5", - azure_os_disk_name: "disk_ce92083f-0041-4825-84b3-6ae8b3525b29", - azure_source_image: "a699494373c04fc0bc8f2bb1389d6106__Win2K8R2SP1-Datacenter-201502.01-en.us-127GB.vhd", - azure_vm_size: "Medium", - azure_storage_account: "chefci", - os_type: "Windows", - connection_protocol: "winrm", - winrm_ssl: true, - winrm_max_timeout: 1_800_000, - winrm_max_memory_per_shell: 600, - } - - deploy = @connection.deploys.create(params) - expect(readFile("create_deployment_winrm.xml")).to eq(@receivedXML) - end - end - end -end diff --git a/spec/unit/roles_list_spec.rb b/spec/unit/roles_list_spec.rb deleted file mode 100755 index 17f7710b..00000000 --- a/spec/unit/roles_list_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") -describe "roles" do - include AzureSpecHelper - include QueryAzureMock - before do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure (@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - it "show all roles" do - roles = @connection.roles.all - roles.each do |role| - expect(role.name).to_not be nil - end - expect(roles.length).to be == 7 - end - specify { expect(@connection.roles.exists?("vm01")).to be true } - specify { expect(@connection.roles.exists?("vm002")).to be true } - specify { expect(@connection.roles.exists?("role001")).to be true } - specify { expect(@connection.roles.exists?("role002")).to be true } - specify { expect(@connection.roles.exists?("role002qqqqq")).to be false } - - it "each role should have values" do - role = @connection.roles.find("vm01") - expect(role.name).to_not be nil - expect(role.status).to_not be nil - expect(role.size).to_not be nil - expect(role.ipaddress).to_not be nil - expect(role.sshport).to_not be nil - expect(role.publicipaddress).to_not be nil - end -end diff --git a/spec/unit/storageaccount_spec.rb b/spec/unit/storageaccount_spec.rb deleted file mode 100644 index 88e69903..00000000 --- a/spec/unit/storageaccount_spec.rb +++ /dev/null @@ -1,86 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require File.expand_path(File.dirname(__FILE__) + "/query_azure_mock") - -describe "storageaccounts" do - include AzureSpecHelper - include QueryAzureMock - - before "setup connection" do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure (@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - context "get all storage accounts" do - specify { expect(@connection.storageaccounts.all.length).to be > 0 } - it "entry fields should not be null" do - items = @connection.storageaccounts.all - items.each do |storageaccount| - expect(storageaccount.name).to_not be nil - end - end - end - - context "check storage account existence" do - it "storage account should exist" do - expect(@connection.storageaccounts.exists?("storage-service-name")).to be true - end - it "storage account should not exist" do - expect(@connection.storageaccounts.exists?("invalid-storage-service-name")).to be false - end - end - - context "create a new storage account" do - it "using explicity parameters it should pass in expected body" do - params = { - azure_dns_name: "service003", - azure_storage_account: "ka001testeurope", - storage_location: "North Europe", - } - storageaccount = @connection.storageaccounts.create(params) - expect(@postname).to be == "storageservices" - expect(@postverb).to be == "post" - expect(@postbody).to eq(readFile("create_storageservice_for_service003.xml")) - end - end - - context "create a new storage account with affinity group" do - it "using explicity parameters it should pass in expected body" do - params = { - azure_dns_name: "service004", - azure_storage_account: "ka001testeurope", - azure_affinity_group: "test-affinity-group", - } - storageaccount = @connection.storageaccounts.create(params) - expect(@postname).to be == "storageservices" - expect(@postverb).to be == "post" - expect(@postbody).to eq(readFile("create_storageservice_for_service004.xml")) - end - end - -end diff --git a/spec/unit/vnet_config_spec.rb b/spec/unit/vnet_config_spec.rb deleted file mode 100644 index 97e1a2f7..00000000 --- a/spec/unit/vnet_config_spec.rb +++ /dev/null @@ -1,1393 +0,0 @@ -# -# Author:: Aliasgar Batterywala () -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" -require_relative "../../lib/azure/resource_management/ARM_interface" - -describe Azure::ARM::VnetConfig do - include QueryAzureMock - module Azure - module ARM - class DummyClass < Azure::ResourceManagement::ARMInterface - include Azure::ARM::VnetConfig - end - end - end - - before do - stub_resource_groups - @dummy_class = Azure::ARM::DummyClass.new - end - - def subnet(resource_group_name, vnet_name, subnet_index = nil) - subnets_list = stub_subnets_list_response(resource_group_name, vnet_name) - - subnet_index.nil? ? subnets_list : subnets_list[subnet_index] - end - - def used_networks(subnets) - used_networks_pool = [] - subnets.each do |subnet| - used_networks_pool.push(IPAddress(subnet.address_prefix)) - end - - used_networks_pool - end - - describe "subnets_list_for_specific_address_space" do - context "subnets exist in the given address_prefix of the virtual network" do - context "example-1" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-2" - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - @subnets_address_prefix = [ subnet(resource_group_name, vnet_name, 0), - subnet(resource_group_name, vnet_name, 2), - ] - end - - it "returns the list of subnets which belongs to the given address_prefix of the virtual network" do - response = @dummy_class.subnets_list_for_specific_address_space("10.2.0.0/16", @subnets) - expect(response.class).to be == Array - expect(response).to be == @subnets_address_prefix - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-1" - vnet_name = "vnet-1" - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - @subnets_address_prefix = [ subnet(resource_group_name, vnet_name, 0), - subnet(resource_group_name, vnet_name, 1), - ] - end - - it "returns the list of subnets which belongs to the given address_prefix of the virtual network" do - response = @dummy_class.subnets_list_for_specific_address_space("10.1.0.0/16", @subnets) - expect(response.class).to be == Array - expect(response).to be == @subnets_address_prefix - end - end - end - - context "no subnets exist in the given address_prefix of the virtual network - example1" do - context "example-1" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-4" - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - @subnets_address_prefix = [] - end - - it "returns the empty list of subnets" do - response = @dummy_class.subnets_list_for_specific_address_space("10.15.0.0/20", @subnets) - expect(response.class).to be == Array - expect(response).to be == @subnets_address_prefix - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-3" - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - end - - it "returns the empty list of subnets" do - response = @dummy_class.subnets_list_for_specific_address_space("141.154.163.0/26", @subnets) - expect(response.class).to be == Array - expect(response.empty?).to be == true - end - end - end - end - - describe "get_vnet" do - context "given vnet exist under the given resource group" do - before do - @resource_group_name = "rgrp-2" - @vnet_name = "vnet-2" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - end - - it "returns vnet object" do - response = @dummy_class.get_vnet(@resource_group_name, @vnet_name) - expect(response.address_space.address_prefixes).to be == [ "10.2.0.0/16", "192.168.172.0/24", "16.2.0.0/24" ] - expect(response.subnets.class).to be == Array - expect(response.subnets.length).to be == 3 - end - end - - context "given vnet does not exist under the given resource group" do - before do - @resource_group_name = "rgrp-2" - @vnet_name = "vnet-22" - request = {} - response = OpenStruct.new({ - "body" => '{"error": {"code": "ResourceNotFound"}}', - }) - body = "MsRestAzure::AzureOperationError" - error = MsRestAzure::AzureOperationError.new(request, response, body) - network_resource_client = double("NetworkResourceClient", - virtual_networks: double) - allow(network_resource_client.virtual_networks).to receive( - :get - ).and_raise(error) - allow(@dummy_class).to receive(:network_resource_client).and_return( - network_resource_client - ) - end - - it "returns false" do - response = @dummy_class.get_vnet(@resource_group_name, @vnet_name) - expect(response).to be == false - end - end - - context "vnet get api call raises some unknown exception" do - before do - @resource_group_name = "rgrp-2" - @vnet_name = "vnet-22" - request = {} - response = OpenStruct.new({ - "body" => '{"error": {"code": "SomeProblemOccurred"}}', - }) - body = "MsRestAzure::AzureOperationError" - @error = MsRestAzure::AzureOperationError.new(request, response, body) - network_resource_client = double("NetworkResourceClient", - virtual_networks: double) - allow(network_resource_client.virtual_networks).to receive( - :get - ).and_raise(@error) - allow(@dummy_class).to receive(:network_resource_client).and_return( - network_resource_client - ) - end - - it "raises error" do - expect do - @dummy_class.get_vnet(@resource_group_name, @vnet_name) - end.to raise_error(@error) - end - end - end - - describe "subnets_list" do - context "when address_prefix is not passed" do - context "example-1" do - before do - @resource_group_name = "rgrp-2" - @vnet_name = "vnet-2" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @subnets_vnet_name = [ subnet(@resource_group_name, @vnet_name) ].flatten! - end - - it "returns a list of all the subnets present under the given virtual network" do - response = @dummy_class.subnets_list(@resource_group_name, @vnet_name) - expect(response.class).to be == Array - expect(response).to be == @subnets_vnet_name - end - end - - context "example-2" do - before do - @resource_group_name = "rgrp-2" - @vnet_name = "vnet-3" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @subnets_vnet_name = [ subnet(@resource_group_name, @vnet_name) ].flatten! - end - - it "returns a list of all the subnets present under the given virtual network" do - response = @dummy_class.subnets_list(@resource_group_name, @vnet_name) - expect(response.class).to be == Array - expect(response).to be == @subnets_vnet_name - end - end - end - - context "when address_prefix is passed" do - context "example-1" do - before do - @resource_group_name = "rgrp-2" - @vnet_name = "vnet-2" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @subnets_address_prefix = [ subnet(@resource_group_name, @vnet_name, 1) ] - end - - it "returns a list of all the subnets present under the given virtual network" do - response = @dummy_class.subnets_list(@resource_group_name, @vnet_name, "192.168.172.0/24") - expect(response.class).to be == Array - expect(response).to be == @subnets_address_prefix - end - end - - context "example-2" do - before do - @resource_group_name = "rgrp-3" - @vnet_name = "vnet-5" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @subnets_address_prefix = [ subnet(@resource_group_name, @vnet_name, 0), - subnet(@resource_group_name, @vnet_name, 1), - subnet(@resource_group_name, @vnet_name, 3), - ] - end - - it "returns a list of all the subnets present under the given virtual network" do - response = @dummy_class.subnets_list(@resource_group_name, @vnet_name, "69.182.8.0/21") - expect(response.class).to be == Array - expect(response).to be == @subnets_address_prefix - end - end - end - end - - describe "subnet" do - before do - @subnet = { - "name" => "my_sbn", - "properties" => { - "addressPrefix" => "10.20.30.40/20", - }, - } - end - - it "returns the hash for subnet" do - response = @dummy_class.subnet("my_sbn", "10.20.30.40/20") - expect(response.class).to be == Hash - expect(response).to be == @subnet - end - end - - describe "vnet_address_spaces" do - context "example-1" do - before do - resource_group_name = "rgrp-1" - vnet_name = "vnet-1" - @vnet = @resource_groups[0][resource_group_name]["vnets"][0][vnet_name] - @address_prefixes = [ "10.1.0.0/16" ] - end - - it "returns address_prefixes for the given virtual network existing under the given resource group" do - response = @dummy_class.vnet_address_spaces(@vnet) - expect(response.class).to be == Array - expect(response).to be == @address_prefixes - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-2" - @vnet = @resource_groups[1][resource_group_name]["vnets"][0][vnet_name] - @address_prefixes = [ "10.2.0.0/16", "192.168.172.0/24", "16.2.0.0/24" ] - end - - it "returns address_prefixes for the given virtual network existing under the given resource group" do - response = @dummy_class.vnet_address_spaces(@vnet) - expect(response.class).to be == Array - expect(response).to be == @address_prefixes - end - end - end - - describe "subnet_address_prefix" do - context "example-1" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-2" - @subnet = subnet(resource_group_name, vnet_name, 1) - end - - it "returns the address_prefix of the subnet present under the given resource_group and vnet_name at the 1st index" do - response = @dummy_class.subnet_address_prefix(@subnet) - expect(response.class).to be == String - expect(response).to be == "192.168.172.0/25" - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-5" - @subnet = subnet(resource_group_name, vnet_name, 4) - end - - it "returns the address_prefix of the subnet present under the given resource_group and vnet_name at the 4th index" do - response = @dummy_class.subnet_address_prefix(@subnet) - expect(response.class).to be == String - expect(response).to be == "12.3.19.128/25" - end - end - end - - describe "sort_available_networks" do - context "example-1" do - before do - @available_networks = [ IPAddress("10.16.48.0/20"), - IPAddress("10.16.32.0/24"), - IPAddress("12.23.19.0/24"), - IPAddress("221.17.234.0/29"), - IPAddress("133.78.152.0/25"), - IPAddress("11.13.48.0/20"), - ] - end - - it "sorts the given pool of available_networks in ascending order of the network's address" do - response = @dummy_class.sort_available_networks(@available_networks) - expect("#{response[0].network.address}/#{response[0].prefix}").to be == "10.16.32.0/24" - expect("#{response[1].network.address}/#{response[1].prefix}").to be == "10.16.48.0/20" - expect("#{response[2].network.address}/#{response[2].prefix}").to be == "11.13.48.0/20" - expect("#{response[3].network.address}/#{response[3].prefix}").to be == "12.23.19.0/24" - expect("#{response[4].network.address}/#{response[4].prefix}").to be == "133.78.152.0/25" - expect("#{response[5].network.address}/#{response[5].prefix}").to be == "221.17.234.0/29" - end - end - - context "example-2" do - before do - @available_networks = [ IPAddress("159.10.0.0/16"), - IPAddress("28.65.42.0/24"), - IPAddress("165.98.0.0/20"), - IPAddress("192.168.172.0/24"), - IPAddress("31.66.12.128/25"), - IPAddress("10.9.0.0/16"), - ] - end - - it "sorts the given pool of available_networks in ascending order of the network's address" do - response = @dummy_class.sort_available_networks(@available_networks) - expect("#{response[0].network.address}/#{response[0].prefix}").to be == "10.9.0.0/16" - expect("#{response[1].network.address}/#{response[1].prefix}").to be == "28.65.42.0/24" - expect("#{response[2].network.address}/#{response[2].prefix}").to be == "31.66.12.128/25" - expect("#{response[3].network.address}/#{response[3].prefix}").to be == "159.10.0.0/16" - expect("#{response[4].network.address}/#{response[4].prefix}").to be == "165.98.0.0/20" - expect("#{response[5].network.address}/#{response[5].prefix}").to be == "192.168.172.0/24" - end - end - end - - describe "sort_subnets_by_cidr_prefix" do - context "example-1" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-2" - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - - resource_group_name = "rgrp-1" - vnet_name = "vnet-1" - @subnets.push(stub_subnets_list_response( - resource_group_name, vnet_name - )).flatten! - end - - it "returns the sorted list of subnets in ascending order of their cidr prefix" do - response = @dummy_class.sort_subnets_by_cidr_prefix(@subnets) - expect(response[0].address_prefix).to be == "10.2.0.0/20" - expect(response[1].address_prefix).to be == "10.1.48.0/20" - expect(response[2].address_prefix).to be == "10.1.0.0/24" - expect(response[3].address_prefix).to be == "192.168.172.0/25" - expect(response[4].address_prefix).to be == "10.2.16.0/28" - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-5" - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - end - - it "returns the sorted list of subnets in ascending order of their cidr prefix" do - response = @dummy_class.sort_subnets_by_cidr_prefix(@subnets) - expect(response[0].address_prefix).to be == "69.182.9.0/24" - expect(response[1].address_prefix).to be == "69.182.11.0/24" - expect(response[2].address_prefix).to be == "69.182.14.0/24" - expect(response[3].address_prefix).to be == "12.3.19.0/25" - expect(response[4].address_prefix).to be == "12.3.19.128/25" - end - end - end - - describe "sort_used_networks_by_hosts_size" do - context "example-1" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-2" - subnets = stub_subnets_list_response(resource_group_name, vnet_name) - - resource_group_name = "rgrp-1" - vnet_name = "vnet-1" - subnets.push(stub_subnets_list_response( - resource_group_name, vnet_name - )).flatten! - - @used_networks_pool = used_networks(subnets) - end - - it "returns the list of used_networks sorted in descending order of their hosts size" do - response = @dummy_class.sort_used_networks_by_hosts_size(@used_networks_pool) - expect(response[0].network.address.concat("/" + response[0].prefix.to_s)).to be == "10.2.0.0/20" - expect(response[1].network.address.concat("/" + response[1].prefix.to_s)).to be == "10.1.48.0/20" - expect(response[2].network.address.concat("/" + response[2].prefix.to_s)).to be == "10.1.0.0/24" - expect(response[3].network.address.concat("/" + response[3].prefix.to_s)).to be == "192.168.172.0/25" - expect(response[4].network.address.concat("/" + response[4].prefix.to_s)).to be == "10.2.16.0/28" - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-4" - subnets = stub_subnets_list_response(resource_group_name, vnet_name) - - resource_group_name = "rgrp-3" - vnet_name = "vnet-5" - subnets.push(stub_subnets_list_response( - resource_group_name, vnet_name - )).flatten! - - @used_networks_pool = used_networks(subnets) - end - - it "returns the list of used_networks sorted in descending order of their hosts size", if: (RUBY_VERSION.to_f <= 2.1) do - response = @dummy_class.sort_used_networks_by_hosts_size(@used_networks_pool) - expect(response[0].network.address.concat("/" + response[0].prefix.to_s)).to be == "69.182.14.0/24" - expect(response[1].network.address.concat("/" + response[1].prefix.to_s)).to be == "69.182.9.0/24" - expect(response[2].network.address.concat("/" + response[2].prefix.to_s)).to be == "69.182.11.0/24" - expect(response[3].network.address.concat("/" + response[3].prefix.to_s)).to be == "12.3.19.128/25" - expect(response[4].network.address.concat("/" + response[4].prefix.to_s)).to be == "12.3.19.0/25" - expect(response[5].network.address.concat("/" + response[5].prefix.to_s)).to be == "40.23.19.0/29" - end - - it "returns the list of used_networks sorted in descending order of their hosts size", if: (RUBY_VERSION.to_f >= 2.2) do - response = @dummy_class.sort_used_networks_by_hosts_size(@used_networks_pool) - expect(response[0].network.address.concat("/" + response[0].prefix.to_s)).to be == "69.182.9.0/24" - expect(response[1].network.address.concat("/" + response[1].prefix.to_s)).to be == "69.182.11.0/24" - expect(response[2].network.address.concat("/" + response[2].prefix.to_s)).to be == "69.182.14.0/24" - expect(response[3].network.address.concat("/" + response[3].prefix.to_s)).to be == "12.3.19.0/25" - expect(response[4].network.address.concat("/" + response[4].prefix.to_s)).to be == "12.3.19.128/25" - expect(response[5].network.address.concat("/" + response[5].prefix.to_s)).to be == "40.23.19.0/29" - end - end - end - - describe "subnet_cidr_prefix" do - context "example-1" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-4" - @subnet = subnet(resource_group_name, vnet_name, 0) - end - - it "returns the cidr prefix of the given subnet" do - response = @dummy_class.subnet_cidr_prefix(@subnet) - expect(response).to be == 29 - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-3" - @subnet = subnet(resource_group_name, vnet_name, 1) - end - - it "returns the cidr prefix of the given subnet" do - response = @dummy_class.subnet_cidr_prefix(@subnet) - expect(response).to be == 25 - end - end - end - - describe "sort_pools" do - it "invokes sort methods for available_networks_pool and used_networks_pool" do - expect(@dummy_class).to receive(:sort_available_networks).and_return([]) - expect(@dummy_class).to receive(:sort_used_networks_by_hosts_size).and_return([]) - response1, response2 = @dummy_class.sort_pools([], []) - expect(response1).to be == [] - expect(response2).to be == [] - end - end - - describe "divide_network" do - context "very large network is passed" do - context "example-1" do - it "divides the network into medium sized subnets" do - response = @dummy_class.divide_network("10.2.0.0/16") - expect(response).to be == "10.2.0.0/20" - end - end - - context "example-2" do - it "divides the network into medium sized subnets" do - response = @dummy_class.divide_network("10.2.0.0/19") - expect(response).to be == "10.2.0.0/20" - end - end - end - - context "medium sized network is passed" do - context "example-1" do - it "divides the network into smaller subnets" do - response = @dummy_class.divide_network("10.2.0.0/22") - expect(response).to be == "10.2.0.0/24" - end - end - - context "example-2" do - it "divides the network into smaller subnets" do - response = @dummy_class.divide_network("10.2.0.0/20") - expect(response).to be == "10.2.0.0/24" - end - end - end - - context "very small network is passed" do - context "example-1" do - it "does not divide the network and keeps it the same" do - response = @dummy_class.divide_network("10.2.0.0/28") - expect(response).to be == "10.2.0.0/28" - end - end - - context "example-2" do - it "does not divide the network and keeps it the same" do - response = @dummy_class.divide_network("10.2.0.0/25") - expect(response).to be == "10.2.0.0/25" - end - end - end - end - - describe "in_use_network?" do - context "subnet_network belongs to available_network" do - context "example-1" do - before do - @subnet_network = IPAddress("79.224.43.229/24") - @available_network = IPAddress("79.224.43.0/24") - end - - it "returns true" do - response = @dummy_class.in_use_network?( - @subnet_network, @available_network - ) - expect(response).to be == true - end - end - - context "example-2" do - before do - @subnet_network = IPAddress("152.23.13.65/24") - @available_network = IPAddress("152.23.0.0/20") - end - - it "returns true" do - response = @dummy_class.in_use_network?( - @subnet_network, @available_network - ) - expect(response).to be == true - end - end - end - - context "available_network belongs to subnet_network" do - context "example-1" do - before do - @subnet_network = IPAddress("79.224.43.0/24") - @available_network = IPAddress("79.224.43.229/24") - end - - it "returns true" do - response = @dummy_class.in_use_network?( - @subnet_network, @available_network - ) - expect(response).to be == true - end - end - - context "example-2" do - before do - @subnet_network = IPAddress("152.23.0.0/20") - @available_network = IPAddress("152.23.13.65/24") - end - - it "returns true" do - response = @dummy_class.in_use_network?( - @subnet_network, @available_network - ) - expect(response).to be == true - end - end - end - - context "none of the network belongs to the other one" do - context "example-1" do - before do - @subnet_network = IPAddress("139.12.78.0/25") - @available_network = IPAddress("139.12.78.231") - end - - it "returns false" do - response = @dummy_class.in_use_network?( - @subnet_network, @available_network - ) - expect(response).to be == false - end - end - - context "example-2" do - before do - @subnet_network = IPAddress("208.140.12.0/24") - @available_network = IPAddress("208.140.10.0/24") - end - - it "returns false" do - response = @dummy_class.in_use_network?( - @subnet_network, @available_network - ) - expect(response).to be == false - end - end - end - end - - describe "new_subnet_address_prefix" do - context "no subnets exist under the given vnet address space" do - it "invokes the divide_network method" do - expect(@dummy_class).to receive(:divide_network) - @dummy_class.new_subnet_address_prefix("", []) - end - - it "invokes divide_network method and return the new subnet prefix value" do - response = @dummy_class.new_subnet_address_prefix("11.23.0.0/16", []) - expect(response).to be == "11.23.0.0/20" - end - - it "invokes divide_network method and return the same vnet prefix value for the new subnet prefix" do - response = @dummy_class.new_subnet_address_prefix("192.168.172.128/25", []) - expect(response).to be == "192.168.172.128/25" - end - end - - context "subnets exist under the given vnet address space" do - context "space available in the vnet address space for the addition of new subnet" do - context "example-1" do - before do - resource_group_name = "rgrp-1" - vnet_name = "vnet-1" - @vnet_address_prefix = "10.1.0.0/16" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, resource_group_name, vnet_name) - ) - @subnets = @dummy_class.subnets_list( - resource_group_name, vnet_name, @vnet_address_prefix - ) - end - - it "returns the address prefix for the new subnet" do - response = @dummy_class.new_subnet_address_prefix( - @vnet_address_prefix, @subnets - ) - expect(response).to be == "10.1.1.0/24" - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-2" - @vnet_address_prefix = "192.168.172.0/24" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, resource_group_name, vnet_name) - ) - @subnets = @dummy_class.subnets_list( - resource_group_name, vnet_name, @vnet_address_prefix - ) - end - - it "returns the address prefix for the new subnet" do - response = @dummy_class.new_subnet_address_prefix( - @vnet_address_prefix, @subnets - ) - expect(response).to be == "192.168.172.128/25" - end - end - - context "example-3" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-5" - @vnet_address_prefix = "69.182.8.0/21" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, resource_group_name, vnet_name) - ) - @subnets = @dummy_class.subnets_list( - resource_group_name, vnet_name, @vnet_address_prefix - ) - end - - it "returns the address prefix for the new subnet" do - response = @dummy_class.new_subnet_address_prefix( - @vnet_address_prefix, @subnets - ) - expect(response).to be == "69.182.8.0/24" - end - end - end - - context "space not available in the vnet address space for the addition of new subnet" do - context "example-1" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-4" - @vnet_address_prefix = "40.23.19.0/29" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, resource_group_name, vnet_name) - ) - @subnets = @dummy_class.subnets_list( - resource_group_name, vnet_name, @vnet_address_prefix - ) - end - - it "returns nil" do - response = @dummy_class.new_subnet_address_prefix( - @vnet_address_prefix, @subnets - ) - expect(response).to eq nil - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-5" - @vnet_address_prefix = "12.3.19.0/24" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, resource_group_name, vnet_name) - ) - @subnets = @dummy_class.subnets_list( - resource_group_name, vnet_name, @vnet_address_prefix - ) - end - - it "returns nil" do - response = @dummy_class.new_subnet_address_prefix( - @vnet_address_prefix, @subnets - ) - expect(response).to eq nil - end - end - - context "example-3" do - before do - @vnet_address_prefix = "62.12.3.128/25" - @subnets = [OpenStruct.new({ "name" => "sbn17", - "address_prefix" => "62.12.3.128/25", - })] - end - - it "returns nil" do - response = @dummy_class.new_subnet_address_prefix( - @vnet_address_prefix, @subnets - ) - expect(response).to eq nil - end - end - end - end - end - - describe "add_subnet" do - context "space does not exist in any of the address_prefixes of the virtual network" do - context "example-1" do - before do - resource_group_name = "rgrp-4" - vnet_name = "vnet-6" - @vnet_config = { virtualNetworkName: vnet_name, - addressPrefixes: [ "130.88.9.0/24", "112.90.2.0/24" ], - subnets: [], - } - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - end - - it "raises error saying no space available to add subnet" do - expect do - @dummy_class.add_subnet("sbn18", - @vnet_config, @subnets) - end .to raise_error(RuntimeError, - "Unable to add subnet sbn18 into the virtual network #{@vnet_config[:virtualNetworkName]}, no address space available !!!") - end - end - - context "example-2" do - before do - @vnet_config = { virtualNetworkName: "vnet-6", - addressPrefixes: [ "10.10.11.0/24" ], - subnets: [], - } - @subnets = [OpenStruct.new({ "name" => "sbn19", - "address_prefix" => "10.10.11.0/25", - }), - OpenStruct.new({ "name" => "sbn20", - "address_prefix" => "10.10.11.128/26", - }), - OpenStruct.new({ "name" => "sbn21", - "address_prefix" => "10.10.11.192/26", - })] - end - - it "raises error saying no space available to add subnet" do - expect do - @dummy_class.add_subnet("sbn22", - @vnet_config, @subnets) - end .to raise_error(RuntimeError, - "Unable to add subnet sbn22 into the virtual network #{@vnet_config[:virtualNetworkName]}, no address space available !!!") - end - end - end - - context "space exist in the virtual network" do - context "example-1" do - before do - resource_group_name = "rgrp-3" - vnet_name = "vnet-4" - @subnet_name = "sbn23" - new_subnet_prefix = "10.15.0.0/24" - @vnet_config = { virtualNetworkName: vnet_name, - addressPrefixes: [ "10.15.0.0/20", "40.23.19.0/29" ], - subnets: [{ "name" => "sbn8", - "properties" => { - "address_prefix" => "40.23.19.0/29", - }, - }], - } - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - @updated_vnet_config = { virtualNetworkName: vnet_name, - addressPrefixes: [ "10.15.0.0/20", "40.23.19.0/29" ], - subnets: [{ "name" => "sbn8", - "properties" => { - "address_prefix" => "40.23.19.0/29", - }, - }, - { "name" => "sbn23", - "properties" => { - "addressPrefix" => new_subnet_prefix, - }, - }], - } - end - - it "returns the updated vnet_config with the hash added for the new subnet" do - response = @dummy_class.add_subnet(@subnet_name, @vnet_config, @subnets) - expect(response).to be == @updated_vnet_config - end - end - - context "example-2" do - before do - resource_group_name = "rgrp-2" - vnet_name = "vnet-2" - @subnet_name = "sbn24" - new_subnet_prefix = "10.2.16.16/28" - @vnet_config = { virtualNetworkName: vnet_name, - addressPrefixes: [ "10.2.0.0/16", "192.168.172.0/24", "16.2.0.0/24" ], - subnets: [{ "name" => "sbn3", - "properties" => { - "address_prefix" => "10.2.0.0/20", - }, - }, - { "name" => "sbn4", - "properties" => { - "address_prefix" => "192.168.172.0/25", - }, - }, - { "name" => "sbn5", - "properties" => { - "address_prefix" => "10.2.16.0/28", - }, - }], - } - @subnets = stub_subnets_list_response(resource_group_name, vnet_name) - @updated_vnet_config = { virtualNetworkName: vnet_name, - addressPrefixes: [ "10.2.0.0/16", "192.168.172.0/24", "16.2.0.0/24" ], - subnets: [{ "name" => "sbn3", - "properties" => { - "address_prefix" => "10.2.0.0/20", - }, - }, - { "name" => "sbn4", - "properties" => { - "address_prefix" => "192.168.172.0/25", - }, - }, - { "name" => "sbn5", - "properties" => { - "address_prefix" => "10.2.16.0/28", - }, - }, - { "name" => "sbn24", - "properties" => { - "addressPrefix" => new_subnet_prefix, - }, - }], - } - end - - it "returns the updated vnet_config with the hash added for the new subnet" do - response = @dummy_class.add_subnet(@subnet_name, @vnet_config, @subnets) - expect(response).to be == @updated_vnet_config - end - end - end - end - - describe "create_vnet_config" do - context "user passed or default named vnet does not exist in the given resource_group" do - before do - @resource_group_name = "rgrp-1" - @vnet_name = "vnet-11" - @subnet_name = "sbn11" - allow(@dummy_class).to receive(:get_vnet).and_return(false) - @vnet_config = { virtualNetworkName: "vnet-11", - addressPrefixes: [ "10.0.0.0/16" ], - subnets: [{ "name" => "sbn11", - "properties" => { - "addressPrefix" => "10.0.0.0/24", - }, - }], - } - end - - it "returns vnet_config with default values for vnet and subnet configurations" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - - context "user passed or default named vnet exist in the given resource_group" do - context "user passed or default named subnet exist in the given virtual network" do - context "example-1" do - before do - @resource_group_name = "rgrp-2" - @vnet_name = "vnet-3" - @subnet_name = "sbn7" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @vnet_config = @vnet_config = { virtualNetworkName: "vnet-3", - addressPrefixes: [ "25.3.16.0/20", "141.154.163.0/26" ], - subnets: [{ "name" => "sbn6", - "properties" => { - "addressPrefix" => "25.3.29.0/25", - }, - }, - { "name" => "sbn7", - "properties" => { - "addressPrefix" => "25.3.29.128/25", - }, - }], - } - end - - it "returns vnet_config with no change" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - - context "example-2" do - before do - @resource_group_name = "rgrp-3" - @vnet_name = "vnet-4" - @subnet_name = "sbn8" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @vnet_config = @vnet_config = { virtualNetworkName: "vnet-4", - addressPrefixes: [ "10.15.0.0/20", "40.23.19.0/29" ], - subnets: [{ "name" => "sbn8", - "properties" => { - "addressPrefix" => "40.23.19.0/29", - }, - }], - } - end - - it "returns vnet_config with no change" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - end - - context "user passed or default named subnet does not exist in the given virtual network" do - context "no space available in the given virtual network to add the new subnet" do - context "example-1" do - before do - @resource_group_name = "rgrp-4" - @vnet_name = "vnet-6" - @subnet_name = "sbn40" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - end - - it "raises error" do - expect do - @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - end.to raise_error(RuntimeError, - "Unable to add subnet #{@subnet_name} into the virtual network #{@vnet_name}, no address space available !!!") - end - end - - context "example-2" do - before do - @resource_group_name = "rgrp-5" - @vnet_name = "vnet-50" - @subnet_name = "sbn60" - - subnets = [OpenStruct.new({ "name" => "sbn19", - "address_prefix" => "10.10.11.0/25", - }), - OpenStruct.new({ "name" => "sbn20", - "address_prefix" => "10.10.11.128/26", - }), - OpenStruct.new({ "name" => "sbn21", - "address_prefix" => "10.10.11.192/26", - })] - - vnet = OpenStruct.new({ - "location" => "westus", - "address_space" => OpenStruct.new({ - "address_prefixes" => [ "10.10.11.0/24" ], - }), - "subnets" => subnets, - }) - - allow(@dummy_class).to receive(:get_vnet).and_return(vnet) - allow(@dummy_class).to receive(:subnets_list).and_return(subnets) - end - - it "raises error" do - expect do - @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - end.to raise_error(RuntimeError, - "Unable to add subnet #{@subnet_name} into the virtual network #{@vnet_name}, no address space available !!!") - end - end - end - - context "space available in the given virtual network to add the new subnet" do - context "example for subnet allocation in first prefix" do - before do - @resource_group_name = "rgrp-3" - @vnet_name = "vnet-5" - @subnet_name = "sbn27" - new_subnet_prefix = "69.182.8.0/24" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @vnet_config = { virtualNetworkName: "vnet-5", - addressPrefixes: [ "69.182.8.0/21", "12.3.19.0/24" ], - subnets: [{ "name" => "sbn9", - "properties" => { - "addressPrefix" => "69.182.9.0/24", - }, - }, - { "name" => "sbn10", - "properties" => { - "addressPrefix" => "69.182.11.0/24", - }, - }, - { "name" => "sbn11", - "properties" => { - "addressPrefix" => "12.3.19.0/25", - }, - }, - { "name" => "sbn12", - "properties" => { - "addressPrefix" => "69.182.14.0/24", - }, - }, - { "name" => "sbn13", - "properties" => { - "addressPrefix" => "12.3.19.128/25", - }, - }, - { "name" => "sbn27", - "properties" => { - "addressPrefix" => new_subnet_prefix, - }, - }], - } - end - - it "returns vnet_config with new subnet added in first prefix" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - - context "example for subnet allocation in subsequent prefix but first" do - before do - @resource_group_name = "rgrp-6" - @vnet_name = "vnet-60" - @subnet_name = "sbn70" - new_subnet_prefix = "133.72.16.128/25" - - subnets = [OpenStruct.new({ "name" => "sbn19", - "address_prefix" => "10.10.11.0/25", - }), - OpenStruct.new({ "name" => "sbn20", - "address_prefix" => "10.10.11.128/26", - }), - OpenStruct.new({ "name" => "sbn21", - "address_prefix" => "10.10.11.192/26", - }), - OpenStruct.new({ "name" => "sbn22", - "address_prefix" => "192.168.172.0/24", - }), - OpenStruct.new({ "name" => "sbn23", - "address_prefix" => "133.72.16.0/25", - })] - - vnet = OpenStruct.new({ - "location" => "westus", - "address_space" => OpenStruct.new({ - "address_prefixes" => [ "10.10.11.0/24", "192.168.172.0/24", "133.72.16.0/24" ], - }), - "subnets" => subnets, - }) - - allow(@dummy_class).to receive(:get_vnet).and_return(vnet) - allow(@dummy_class).to receive(:subnets_list).and_return(subnets) - - @vnet_config = { virtualNetworkName: "vnet-60", - addressPrefixes: [ "10.10.11.0/24", "192.168.172.0/24", "133.72.16.0/24" ], - subnets: [{ "name" => "sbn19", - "properties" => { - "addressPrefix" => "10.10.11.0/25", - }, - }, - { "name" => "sbn20", - "properties" => { - "addressPrefix" => "10.10.11.128/26", - }, - }, - { "name" => "sbn21", - "properties" => { - "addressPrefix" => "10.10.11.192/26", - }, - }, - { "name" => "sbn22", - "properties" => { - "addressPrefix" => "192.168.172.0/24", - }, - }, - { "name" => "sbn23", - "properties" => { - "addressPrefix" => "133.72.16.0/25", - }, - }, - { "name" => "sbn70", - "properties" => { - "addressPrefix" => new_subnet_prefix, - }, - }], - } - end - - it "returns vnet_config with new subnet added in subsequent prefix but first" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - - context "divide_network example" do - before do - @resource_group_name = "rgrp-3" - @vnet_name = "vnet-4" - @subnet_name = "sbn26" - new_subnet_prefix = "10.15.0.0/24" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @vnet_config = @vnet_config = { virtualNetworkName: "vnet-4", - addressPrefixes: [ "10.15.0.0/20", "40.23.19.0/29" ], - subnets: [{ "name" => "sbn8", - "properties" => { - "addressPrefix" => "40.23.19.0/29", - }, - }, - { "name" => "sbn26", - "properties" => { - "addressPrefix" => new_subnet_prefix, - }, - }], - } - end - - it "returns vnet_config with new subnet added" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - end - end - end - end - - describe "GatewaySubnet" do - context "user provided or default named virtual network exist along with GatewaySubnet and other subnets also present" do - context "user provided or default named subnet does not exist in the virtual network" do - before do - @resource_group_name = "rgrp-4" - @vnet_name = "vnet-7" - @subnet_name = "sbn30" - new_subnet_prefix = "10.3.0.0/24" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @vnet_config = { virtualNetworkName: "vnet-7", - addressPrefixes: [ "10.3.0.0/16", "160.10.2.0/24" ], - subnets: [{ "name" => "sbn15", - "properties" => { - "addressPrefix" => "160.10.2.192/26", - }, - }, - { "name" => "GatewaySubnet", - "properties" => { - "addressPrefix" => "10.3.1.0/24", - }, - }, - { "name" => "sbn16", - "properties" => { - "addressPrefix" => "160.10.2.0/25", - }, - }, - { "name" => "sbn30", - "properties" => { - "addressPrefix" => new_subnet_prefix, - }, - }], - } - end - - it "returns vnet_config with GatewaySubnet along with other subnets preserved and also new subnet added" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - - context "user provided or default named subnet exist in the virtual network" do - before do - @resource_group_name = "rgrp-4" - @vnet_name = "vnet-7" - @subnet_name = "sbn16" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - @vnet_config = { virtualNetworkName: "vnet-7", - addressPrefixes: [ "10.3.0.0/16", "160.10.2.0/24" ], - subnets: [{ "name" => "sbn15", - "properties" => { - "addressPrefix" => "160.10.2.192/26", - }, - }, - { "name" => "GatewaySubnet", - "properties" => { - "addressPrefix" => "10.3.1.0/24", - }, - }, - { "name" => "sbn16", - "properties" => { - "addressPrefix" => "160.10.2.0/25", - }, - }], - } - end - - it "returns vnet_config with no change" do - response = @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - expect(response).to be == @vnet_config - end - end - end - - context "user provided or default named subnet value is GatewaySubnet" do - it "raises error" do - expect do - @dummy_class.create_vnet_config( - "rgrp-1", "vnet-1", "GatewaySubnet" - ) - end.to raise_error(ArgumentError, "GatewaySubnet cannot be used as the name for --azure-vnet-subnet-name option. GatewaySubnet can only be used for virtual network gateways.") - end - end - - context "user provided or default named subnet value is not GatewaySubnet" do - before do - @resource_group_name = "rgrp-4" - @vnet_name = "vnet-7" - @subnet_name = "sbn26" - allow(@dummy_class).to receive(:network_resource_client).and_return( - stub_network_resource_client(nil, @resource_group_name, @vnet_name) - ) - end - - it "does not raise error" do - expect do - @dummy_class.create_vnet_config( - @resource_group_name, @vnet_name, @subnet_name - ) - end .to_not raise_error - end - end - end -end diff --git a/spec/unit/vnet_spec.rb b/spec/unit/vnet_spec.rb deleted file mode 100644 index c173655e..00000000 --- a/spec/unit/vnet_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -# -# Copyright:: Copyright (c) Chef Software 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. -# - -require_relative "../spec_helper" -require_relative "query_azure_mock" - -describe "vnets" do - include AzureSpecHelper - include QueryAzureMock - - before "setup connection" do - @server_instance = Chef::Knife::AzureServerCreate.new - { - azure_subscription_id: "azure_subscription_id", - azure_mgmt_cert: @cert_file, - azure_api_host_name: "preview.core.windows-int.net", - }.each do |key, value| - @server_instance.config[key] = value - end - - stub_query_azure (@server_instance.service.connection) - @connection = @server_instance.service.connection - end - - context "mock with actually retrieved values" do - it "should find strings" do - items = @connection.vnets.all - expect(items.length).to be > 1 - items.each do |vnet| - expect(vnet.name).to_not be nil - expect(vnet.affinity_group).to_not be nil - expect(vnet.state).to_not be nil - end - end - - it "should find correct vnets." do - expect(@connection.vnets.exists?("jm-vnet-test")).to eq(true) - expect(@connection.vnets.exists?("not-there")).to eq(false) - end - - it "should contain Created state" do - @connection.vnets.all.each do |item| - expect(item.state).to eq("Created") - end - end - end - - context "create should" do - it "create a vnet that does not already exist" do - params = { - azure_vnet_name: "new-vn", - azure_ag_name: "someag", - azure_address_space: "10.0.0.0/16", - azure_subnet_name: "new-sb", - } - @connection.vnets.create(params) - expect(@postname).to eq("networking/media") - expect(@postverb).to eq("put") - expect(@postbody).to eq(readFile("set_network_new.xml")) - end - it "modify an existing vnet" do - params = { - azure_vnet_name: "vnname", - azure_ag_name: "new-agname", - azure_address_space: "192.168.0.0/20", - azure_subnet_name: "new-sb", - } - @connection.vnets.create(params) - expect(@postname).to eq("networking/media") - expect(@postverb).to eq("put") - expect(@postbody).to eq(readFile("set_network_existing.xml")) - end - end -end diff --git a/spec/unit/windows_credentials_spec.rb b/spec/unit/windows_credentials_spec.rb index 629f5633..18f21c3b 100644 --- a/spec/unit/windows_credentials_spec.rb +++ b/spec/unit/windows_credentials_spec.rb @@ -17,11 +17,9 @@ # require_relative "../spec_helper" -require_relative "query_azure_mock" describe Chef::Knife::AzurermBase, :windows_only do include AzureSpecHelper - include QueryAzureMock class Chef class Knife From a2516c069acde00a3d6b499119f4ab0678b1ea50 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 2 Jun 2021 15:03:06 -0700 Subject: [PATCH 04/10] Remove extra test deps and move rb-readline to a gem dep Signed-off-by: Tim Smith --- Gemfile | 5 ----- knife-azure.gemspec | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index fa77a084..20e1d3e7 100644 --- a/Gemfile +++ b/Gemfile @@ -6,12 +6,7 @@ group :test do gem "activesupport", "6.0.3.3" gem "chefstyle" gem "equivalent-xml", "~> 0.6.0" - gem "guard-rspec" - gem "knife-cloud", ">= 1.0.0" - gem "mixlib-config", ">= 3.0", "< 5" - gem "mixlib-shellout" gem "rake" - gem "rb-readline" gem "rspec", ">= 3.0" gem "rspec_junit_formatter" end diff --git a/knife-azure.gemspec b/knife-azure.gemspec index ada79e65..a0694e04 100755 --- a/knife-azure.gemspec +++ b/knife-azure.gemspec @@ -26,4 +26,5 @@ Gem::Specification.new do |s| s.add_dependency "azure_mgmt_network", "~> 0.18", ">= 0.18.2" s.add_dependency "listen", "~> 3.1" s.add_dependency "ipaddress" + s.add_dependency "rb-readline" end From c7534abd62cb467f1cba37cc3125e68a17978778 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 2 Jun 2021 15:04:24 -0700 Subject: [PATCH 05/10] Add Ruby 3 testing Signed-off-by: Tim Smith --- .expeditor/verify.pipeline.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.expeditor/verify.pipeline.yml b/.expeditor/verify.pipeline.yml index 2bf9cfaa..8fe2c21a 100644 --- a/.expeditor/verify.pipeline.yml +++ b/.expeditor/verify.pipeline.yml @@ -18,6 +18,15 @@ steps: executor: docker: image: ruby:2.7-buster + +- label: run-specs-ruby-3.0 + command: + - .expeditor/run_linux_tests.sh rake + expeditor: + executor: + docker: + image: ruby:3.0-buster + - label: run-specs-windows command: - bundle config --local path vendor/bundle From 6197964ef58ce39150297acb1f8f0a5d7b9c251b Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 2 Jun 2021 15:04:59 -0700 Subject: [PATCH 06/10] Remove more test deps Signed-off-by: Tim Smith --- Gemfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gemfile b/Gemfile index 20e1d3e7..78ae65d1 100644 --- a/Gemfile +++ b/Gemfile @@ -3,9 +3,7 @@ source "https://rubygems.org" gemspec group :test do - gem "activesupport", "6.0.3.3" gem "chefstyle" - gem "equivalent-xml", "~> 0.6.0" gem "rake" gem "rspec", ">= 3.0" gem "rspec_junit_formatter" From 9e98920952504a0809605852f962b4ef41ed2690 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 2 Jun 2021 15:06:42 -0700 Subject: [PATCH 07/10] Add back nokogiri and ffi deps Signed-off-by: Tim Smith --- knife-azure.gemspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/knife-azure.gemspec b/knife-azure.gemspec index a0694e04..9def2fcf 100755 --- a/knife-azure.gemspec +++ b/knife-azure.gemspec @@ -20,11 +20,13 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.7" s.add_dependency "knife" + s.add_dependency "nokogiri", ">= 1.5.5" s.add_dependency "azure_mgmt_resources", "~> 0.17", ">= 0.17.2" s.add_dependency "azure_mgmt_compute", "~> 0.18", ">= 0.18.3" s.add_dependency "azure_mgmt_storage", "~> 0.20", ">= 0.20.0" s.add_dependency "azure_mgmt_network", "~> 0.18", ">= 0.18.2" s.add_dependency "listen", "~> 3.1" s.add_dependency "ipaddress" + s.add_dependency "ffi" s.add_dependency "rb-readline" end From 57f0d91e51ca5db799b9fb531a1334271ec39878 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 2 Jun 2021 15:38:38 -0700 Subject: [PATCH 08/10] Update error message Signed-off-by: Tim Smith --- lib/chef/knife/helpers/azurerm_base.rb | 4 ++-- spec/unit/azurerm_base_spec.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/chef/knife/helpers/azurerm_base.rb b/lib/chef/knife/helpers/azurerm_base.rb index a4a8ff2c..010d9dec 100644 --- a/lib/chef/knife/helpers/azurerm_base.rb +++ b/lib/chef/knife/helpers/azurerm_base.rb @@ -76,7 +76,7 @@ def validate_arm_keys!(*keys) errors = [] keys.each do |k| if config[k].nil? - errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your knife.rb." + errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your config.rb (knife.rb)." end end if errors.each { |e| ui.error(e) }.any? @@ -349,7 +349,7 @@ def login_message ## in Windows Credentails Manager (WCM). ## Newer versions use the same pattern across platforms where credentials gets ## stored in ~/.azure/accessTokens.json file. - "Please run XPLAT's '#{@azure_prefix} login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your knife.rb" + "Please run XPLAT's '#{@azure_prefix} login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your config.rb (knife.rb)." end end end diff --git a/spec/unit/azurerm_base_spec.rb b/spec/unit/azurerm_base_spec.rb index 4f0ba987..ecc8326a 100644 --- a/spec/unit/azurerm_base_spec.rb +++ b/spec/unit/azurerm_base_spec.rb @@ -107,7 +107,7 @@ def validate_cert end it "Accesstoken file doesnt exist for Linux" do allow(File).to receive(:exist?).and_return(false) - expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your knife.rb") + expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your config.rb (knife.rb).") end it "Accesstoken file exist for Linux" do @@ -118,7 +118,7 @@ def validate_cert it "Accesstoken file contain [] value upon running azure logout command for Linux" do allow(File).to receive(:size?).and_return(2) - expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your knife.rb") + expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your config.rb (knife.rb).") end end @@ -164,7 +164,7 @@ def validate_cert expect(Mixlib::ShellOut).to receive(:new).with( "cmdkey /list | findstr AzureXplatCli" ).and_return(xplat_creds_cmd) - expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your knife.rb") + expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your config.rb (knife.rb).") end end @@ -198,7 +198,7 @@ def validate_cert it "raises error" do expect(Mixlib::ShellOut).to_not receive(:new) expect(File).to receive(:expand_path).and_return("user_home_path") - expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your knife.rb") + expect { @arm_server_instance.validate_azure_login }.to raise_error("Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your config.rb (knife.rb).") end end From d8480782861a39c35e5f79998d0e5ab02bc76036 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Thu, 3 Jun 2021 16:21:33 +0000 Subject: [PATCH 09/10] Bump version to 4.0.0 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- lib/knife-azure/version.rb | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3a057be..cba7780b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # knife-azure Change Log - -## [v3.0.8](https://github.com/chef/knife-azure/tree/v3.0.8) (2021-06-02) + +## [v4.0.0](https://github.com/chef/knife-azure/tree/v4.0.0) (2021-06-03) #### Merged Pull Requests -- Upgrade to GitHub-native Dependabot [#544](https://github.com/chef/knife-azure/pull/544) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) +- Remove the legacy azure commands that use the new deprecated API [#548](https://github.com/chef/knife-azure/pull/548) ([tas50](https://github.com/tas50)) ### Changes not yet released to rubygems.org #### Merged Pull Requests +- Remove the legacy azure commands that use the new deprecated API [#548](https://github.com/chef/knife-azure/pull/548) ([tas50](https://github.com/tas50)) - Upgrade to GitHub-native Dependabot [#544](https://github.com/chef/knife-azure/pull/544) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) - Update activesupport requirement from 6.0.3.1 to 6.0.3.3 [#536](https://github.com/chef/knife-azure/pull/536) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) diff --git a/VERSION b/VERSION index e46454be..0c89fc92 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0.8 \ No newline at end of file +4.0.0 \ No newline at end of file diff --git a/lib/knife-azure/version.rb b/lib/knife-azure/version.rb index abc99191..7dd3f7cf 100755 --- a/lib/knife-azure/version.rb +++ b/lib/knife-azure/version.rb @@ -17,7 +17,7 @@ module Knife module Azure - VERSION = "3.0.8".freeze + VERSION = "4.0.0".freeze MAJOR, MINOR, TINY = VERSION.split(".") end end From 2681c6ba2ce2ea35510825717826a024e700ca86 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Thu, 3 Jun 2021 16:23:35 +0000 Subject: [PATCH 10/10] Update CHANGELOG.md to reflect the promotion of 4.0.0 Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cba7780b..6ebf5509 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,24 @@ # knife-azure Change Log - -## [v4.0.0](https://github.com/chef/knife-azure/tree/v4.0.0) (2021-06-03) - -#### Merged Pull Requests -- Remove the legacy azure commands that use the new deprecated API [#548](https://github.com/chef/knife-azure/pull/548) ([tas50](https://github.com/tas50)) + - -### Changes not yet released to rubygems.org - -#### Merged Pull Requests -- Remove the legacy azure commands that use the new deprecated API [#548](https://github.com/chef/knife-azure/pull/548) ([tas50](https://github.com/tas50)) -- Upgrade to GitHub-native Dependabot [#544](https://github.com/chef/knife-azure/pull/544) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) -- Update activesupport requirement from 6.0.3.1 to 6.0.3.3 [#536](https://github.com/chef/knife-azure/pull/536) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) + +## [v4.0.0](https://github.com/chef/knife-azure/tree/v4.0.0) (2021-06-03) + +#### Merged Pull Requests +- Update activesupport requirement from 6.0.3.1 to 6.0.3.3 [#536](https://github.com/chef/knife-azure/pull/536) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) +- Upgrade to GitHub-native Dependabot [#544](https://github.com/chef/knife-azure/pull/544) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) +- Remove the legacy azure commands that use the new deprecated API [#548](https://github.com/chef/knife-azure/pull/548) ([tas50](https://github.com/tas50)) + + ## [v3.0.6](https://github.com/chef/knife-azure/tree/v3.0.6) (2020-09-09) #### Merged Pull Requests - improve load performance by autoloading ARMInterface [#534](https://github.com/chef/knife-azure/pull/534) ([mwrock](https://github.com/mwrock)) - ## [v3.0.5](https://github.com/chef/knife-azure/tree/v3.0.5) (2020-08-21)