From 90e6ae1a3f70656fb03f8ced6d31849bc9d71515 Mon Sep 17 00:00:00 2001 From: Gene Liverman Date: Wed, 1 Dec 2021 15:37:48 -0500 Subject: [PATCH 01/70] Fix naming, add docs, add missing test file --- .rubocop.yml | 98 ++++++ .rubocop_todo.yml | 286 ++++++++++++++++++ Gemfile.lock | 19 +- LICENSE | 201 ++++++++++++ README.md | 14 +- .../version.rb | 2 +- spec/unit/providers_spec.rb | 44 +++ ...mspec => vmpooler-provider-vsphere.gemspec | 6 +- 8 files changed, 662 insertions(+), 8 deletions(-) create mode 100644 .rubocop.yml create mode 100644 .rubocop_todo.yml create mode 100644 LICENSE rename lib/{vmpooler-vsphere-provider => vmpooler-provider-vsphere}/version.rb (63%) create mode 100644 spec/unit/providers_spec.rb rename vmpooler-vsphere-provider.gemspec => vmpooler-provider-vsphere.gemspec (88%) diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..0f38ae9 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,98 @@ +inherit_from: .rubocop_todo.yml + +AllCops: + Include: + - 'lib/**/*.rb' + Exclude: + - 'scripts/**/*' + - 'spec/**/*' + - 'vendor/**/*' + - Gemfile + - Rakefile + - Vagrantfile + +Style/Documentation: + Enabled: false + +# Line length is not useful +Layout/LineLength: + Enabled: false + +# Empty method definitions over more than one line is ok +Style/EmptyMethod: + Enabled: false + +# Due to legacy codebase +# - Globals are used liberally +Style/GlobalVars: + Enabled: false +# - A lot of complexity +Metrics/AbcSize: + Enabled: false +Metrics/PerceivedComplexity: + Enabled: false +Metrics/CyclomaticComplexity: + Enabled: false +Metrics/BlockNesting: + Enabled: false +# - Long Methods, Classes, Blocks, and Modules +Metrics/MethodLength: + Enabled: false +Metrics/ClassLength: + Enabled: false +Metrics/BlockLength: + Enabled: false +Metrics/ModuleLength: + Enabled: false + +# WordArray is crashing rubocop in lib/vmpooler/api/helpers.rb +Style/WordArray: + Enabled: false + +# RedundantBegin is causing lib/pool_manager & vsphere.rb to fail in Ruby 2.5+ +Style/RedundantBegin: + Enabled: false + +# Either sytnax for regex is ok +Style/RegexpLiteral: + Enabled: false + +# In some cases readability is better without these cops enabled +Style/ConditionalAssignment: + Enabled: false +Style/Next: + Enabled: false +Metrics/ParameterLists: + Max: 10 +Style/GuardClause: + Enabled: false + +# Enforce LF line endings, even when on Windows +Layout/EndOfLine: + EnforcedStyle: lf + +# Added in 0.80, don't really care about the change +Style/HashEachMethods: + Enabled: false + +# Added in 0.80, don't really care about the change +Style/HashTransformKeys: + Enabled: false + +# Added in 0.80, don't really care about the change +Style/HashTransformValues: + Enabled: false + +# These short variable names make sense as exceptions to the rule, but generally I think short variable names do hurt readability +Naming/MethodParameterName: + AllowedNames: + - vm + - dc + - s + - x + - f + +# Standard comparisons seem more readable +Style/NumericPredicate: + Enabled: true + EnforcedStyle: comparison diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..6105e46 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,286 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2017-07-18 15:25:35 -0700 using RuboCop version 0.49.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: with_first_parameter, with_fixed_indentation +Layout/ParameterAlignment: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 9 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentOneStep, IndentationWidth. +# SupportedStyles: case, end +Layout/CaseIndentation: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/ClosingParenthesisIndentation: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/EmptyLines: + Exclude: + - 'lib/vmpooler/api/dashboard.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines +Layout/EmptyLinesAroundClassBody: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines +Layout/EmptyLinesAroundModuleBody: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/FirstHashElementIndentation: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: Width, IgnoredPatterns. +Layout/IndentationWidth: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: space, no_space +Layout/SpaceAroundEqualsInParameterDefault: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/SpaceAroundKeyword: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceAroundOperators: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideHashLiteralBraces: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/SpaceInsideParens: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 2 +# Configuration parameters: AllowSafeAssignment. +Lint/AssignmentInCondition: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 2 +Lint/SuppressedException: + Exclude: + - 'lib/vmpooler/api/dashboard.rb' + +# Offense count: 1 +Lint/ShadowingOuterLocalVariable: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. +Lint/UnusedMethodArgument: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 6 +Lint/UselessAssignment: + Exclude: + - 'lib/vmpooler/api/dashboard.rb' + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 6 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: always, conditionals +Style/AndOr: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +Style/CaseEquality: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 1 +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: for, each +Style/For: + Exclude: + - 'lib/vmpooler/api/dashboard.rb' + +# Offense count: 24 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. +# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys +Style/HashSyntax: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: MaxLineLength. +Style/IfUnlessModifier: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: both, prefix, postfix +Style/NegatedIf: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +Style/Not: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. +# SupportedStyles: predicate, comparison +Style/NumericPredicate: + Exclude: + - 'spec/**/*' + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Style/ParallelAssignment: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowSafeAssignment. +Style/ParenthesesAroundCondition: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Style/PerlBackrefs: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. +# NamePrefix: is_, has_, have_ +# NamePrefixBlacklist: is_, has_, have_ +# NameWhitelist: is_a? +Naming/PredicateName: + Exclude: + - 'spec/**/*' + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/RedundantParentheses: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: AllowMultipleReturnValues. +Style/RedundantReturn: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +Style/RedundantSelf: + Exclude: + - 'lib/vmpooler/api.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment. +# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex +Style/TernaryParentheses: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 2 +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: snake_case, camelCase +Naming/VariableName: + Exclude: + - 'lib/vmpooler/api/v1.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/YodaCondition: + Exclude: + - 'lib/vmpooler/api/helpers.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/ZeroLengthPredicate: + Exclude: + - 'lib/vmpooler/api/helpers.rb' diff --git a/Gemfile.lock b/Gemfile.lock index 83f626e..de44d82 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-vsphere-provider (1.3.0) + vmpooler-provider-vsphere (1.3.0) rbvmomi (>= 2.1, < 4.0) GEM @@ -35,9 +35,11 @@ GEM faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) + ffi (1.15.4-java) google-cloud-env (1.5.0) faraday (>= 0.17.3, < 2.0) json (2.6.1) + json (2.6.1-java) method_source (1.0.0) mock_redis (0.29.0) ruby2_keywords @@ -46,6 +48,9 @@ GEM ruby2_keywords (~> 0.0.1) net-ldap (0.17.0) nio4r (2.5.8) + nio4r (2.5.8-java) + nokogiri (1.12.5-java) + racc (~> 1.4) nokogiri (1.12.5-x86_64-linux) racc (~> 1.4) opentelemetry-api (0.17.0) @@ -84,9 +89,16 @@ GEM pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) + pry (0.14.1-java) + coderay (~> 1.1) + method_source (~> 1.0) + spoon (~> 0.0) puma (5.5.2) nio4r (~> 2.0) + puma (5.5.2-java) + nio4r (~> 2.0) racc (1.6.0) + racc (1.6.0-java) rack (2.2.3) rack-protection (2.1.0) rack @@ -141,6 +153,8 @@ GEM tilt (~> 2.0) spicy-proton (2.1.13) bindata (~> 2.3) + spoon (0.0.6) + ffi statsd-ruby (1.5.0) thor (1.1.0) thrift (0.15.0) @@ -172,6 +186,7 @@ GEM rspec (~> 3) PLATFORMS + universal-java-11 x86_64-linux DEPENDENCIES @@ -184,7 +199,7 @@ DEPENDENCIES simplecov (>= 0.11.2) thor (~> 1.0, >= 1.0.1) vmpooler (~> 1.3, >= 1.3.0) - vmpooler-vsphere-provider! + vmpooler-provider-vsphere! yarjuf (>= 2.0) BUNDLED WITH diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md index 8cbda60..57bd71e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ -# vmpooler-vsphere-provider +# vmpooler-provider-vsphere -This is a WIP - do not use yet. The goal is to extract the vSphere provider from the main VMPooler codebase. Force pushes to this repo may happen while initial development is happening. +This is a provider for [VMPooler](https://github.com/puppetlabs/vmpooler) allows using vSphere as a source of machines. This provider was originally part of the main VMPooler code base but was extracted to be a standalone gem so that development could be done independently of VMPooler itself. + +## Usage + +Include this gem in the same Gemfile that you use to install VMPooler itself and then define one or more pools with the `provider` key set to `vsphere`. VMPooler will take care of the rest. + +Examples of deploying VMPooler with this provider can be found in the [puppetlabs/vmpooler-deployment](https://github.com/puppetlabs/vmpooler-deployment) repository. + +## License + +vmpooler-provider-vsphere is distributed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). See the [LICENSE](LICENSE) file for more details. diff --git a/lib/vmpooler-vsphere-provider/version.rb b/lib/vmpooler-provider-vsphere/version.rb similarity index 63% rename from lib/vmpooler-vsphere-provider/version.rb rename to lib/vmpooler-provider-vsphere/version.rb index e9c9fad..cf3b1f5 100644 --- a/lib/vmpooler-vsphere-provider/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -module VmpoolerVsphereProvider +module VmpoolerProviderVsphere VERSION = '1.3.0' end diff --git a/spec/unit/providers_spec.rb b/spec/unit/providers_spec.rb new file mode 100644 index 0000000..ab7b28e --- /dev/null +++ b/spec/unit/providers_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' +require 'vmpooler/providers' + +describe 'providers' do + + let(:providers) do + Vmpooler::Providers.new + end + + it '#correct class' do + expect(providers).to be_a Vmpooler::Providers + end + + it '#load_all_providers' do + expect(Vmpooler::Providers.load_all_providers.join(', ')).to match(%r{/lib/vmpooler/providers/base.rb}) + expect(Vmpooler::Providers.load_all_providers.join(', ')).to match(%r{/lib/vmpooler/providers/dummy.rb}) + expect(Vmpooler::Providers.load_all_providers.join(', ')).to match(%r{#{project_root_dir}/lib/vmpooler/providers/vsphere.rb}) + end + + it '#installed_providers' do + expect(Vmpooler::Providers.installed_providers).to eq(['vmpooler', 'vmpooler-provider-vsphere']) + end + + it '#load_by_name' do + expect(Vmpooler::Providers.load_by_name('vsphere').join(', ')).to match(%r{#{project_root_dir}/lib/vmpooler/providers/vsphere.rb}) + expect(Vmpooler::Providers.load_by_name('vsphere').join(', ')).to_not match(%r{base.rb}) + expect(Vmpooler::Providers.load_by_name('vsphere').join(', ')).to_not match(%r{dummy.rb}) + end + + it '#load only vpshere' do + expect(providers.load_from_gems('vsphere').join(', ')).to match(%r{#{project_root_dir}/lib/vmpooler/providers/vsphere.rb}) + expect(providers.load_from_gems('vsphere').join(', ')).to_not match(%r{base.rb}) + expect(providers.load_from_gems('vsphere').join(', ')).to_not match(%r{dummy.rb}) + end + + it '#load all providers from gems' do + expect(providers.load_from_gems.join(', ')).to match(%r{/lib/vmpooler/providers/base.rb}) + expect(providers.load_from_gems.join(', ')).to match(%r{/lib/vmpooler/providers/dummy.rb}) + expect(providers.load_from_gems.join(', ')).to match(%r{#{project_root_dir}/lib/vmpooler/providers/vsphere.rb}) + + end + + +end diff --git a/vmpooler-vsphere-provider.gemspec b/vmpooler-provider-vsphere.gemspec similarity index 88% rename from vmpooler-vsphere-provider.gemspec rename to vmpooler-provider-vsphere.gemspec index dc48dd3..9b078db 100644 --- a/vmpooler-vsphere-provider.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -1,10 +1,10 @@ lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'vmpooler-vsphere-provider/version' +require 'vmpooler-provider-vsphere/version' Gem::Specification.new do |s| - s.name = 'vmpooler-vsphere-provider' - s.version = VmpoolerVsphereProvider::VERSION + s.name = 'vmpooler-provider-vsphere' + s.version = VmpoolerProviderVsphere::VERSION s.authors = ['Puppet'] s.email = ['support@puppet.com'] From 3407e500f15cad63c444cfc83fb95042835062d4 Mon Sep 17 00:00:00 2001 From: Gene Liverman Date: Fri, 3 Dec 2021 10:44:00 -0500 Subject: [PATCH 02/70] Add GH Action for releasing gems --- .github/workflows/release.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9fb3aad --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release + +on: workflow_dispatch + +jobs: + release: + runs-on: ubuntu-latest + if: github.repository == 'puppetlabs/vmpooler-provider-vsphere' + steps: + - uses: actions/checkout@v2 + - name: Get Version + id: gv + run: | + echo "::set-output name=ver::$(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev)" + - name: Tag Release + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.gv.outputs.ver }} + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + generateReleaseNotes: true + - name: Install Ruby 2.5.8 + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.5.8' + - name: Build gem + run: gem build *.gemspec + - name: Publish gem + run: gem push *.gem + env: + GEM_HOST_API_KEY: '${{ secrets.RUBYGEMS_AUTH_TOKEN }}' From ceb5cda1843fa77f6cb94f4909b9eb59946f6be1 Mon Sep 17 00:00:00 2001 From: Gene Liverman Date: Wed, 8 Dec 2021 09:43:41 -0500 Subject: [PATCH 03/70] Use credentials file for Rubygems auth --- .github/workflows/release.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9fb3aad..ac38a53 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,11 @@ jobs: - name: Build gem run: gem build *.gemspec - name: Publish gem - run: gem push *.gem + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + gem push *.gem env: GEM_HOST_API_KEY: '${{ secrets.RUBYGEMS_AUTH_TOKEN }}' From b1b0abb6c287229378ea4ebd3836319afd99dbc9 Mon Sep 17 00:00:00 2001 From: Gene Liverman Date: Wed, 8 Dec 2021 10:00:39 -0500 Subject: [PATCH 04/70] Require VMPooler 2.0 or greater VMPooler 2.0.0 was the first release without the code contained in this respository. --- Gemfile.lock | 13 ++++++------- lib/vmpooler-provider-vsphere/version.rb | 2 +- vmpooler-provider-vsphere.gemspec | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index de44d82..5bde0f7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (1.3.0) + vmpooler-provider-vsphere (1.4.0) rbvmomi (>= 2.1, < 4.0) GEM @@ -82,7 +82,7 @@ GEM opentelemetry-instrumentation-base (~> 0.17.0) optimist (3.0.1) parallel (1.21.0) - parser (3.0.3.1) + parser (3.0.3.2) ast (~> 2.4.1) pickup (0.0.11) prometheus-client (2.1.0) @@ -112,7 +112,7 @@ GEM nokogiri (~> 1.10) optimist (~> 3.0) redis (4.5.1) - regexp_parser (2.1.1) + regexp_parser (2.2.0) rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -136,7 +136,7 @@ GEM rubocop-ast (>= 1.0.1) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.13.0) + rubocop-ast (1.14.0) parser (>= 3.0.1.1) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) @@ -160,7 +160,7 @@ GEM thrift (0.15.0) tilt (2.0.10) unicode-display_width (1.8.0) - vmpooler (1.3.0) + vmpooler (2.0.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) net-ldap (~> 0.16) @@ -176,7 +176,6 @@ GEM puma (~> 5.0, >= 5.0.4) rack (~> 2.2) rake (~> 13.0) - rbvmomi (>= 2.1, < 4.0) redis (~> 4.1) sinatra (~> 2.0) spicy-proton (~> 2.1) @@ -198,7 +197,7 @@ DEPENDENCIES rubocop (~> 1.1.0) simplecov (>= 0.11.2) thor (~> 1.0, >= 1.0.1) - vmpooler (~> 1.3, >= 1.3.0) + vmpooler (~> 2.0) vmpooler-provider-vsphere! yarjuf (>= 2.0) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index cf3b1f5..234751f 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '1.3.0' + VERSION = '1.4.0' end diff --git a/vmpooler-provider-vsphere.gemspec b/vmpooler-provider-vsphere.gemspec index 9b078db..a0d332b 100644 --- a/vmpooler-provider-vsphere.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.require_paths = ["lib"] s.add_dependency 'rbvmomi', '>= 2.1', '< 4.0' - s.add_development_dependency 'vmpooler', '~> 1.3', '>= 1.3.0' + s.add_development_dependency 'vmpooler', '~> 2.0' # Testing dependencies s.add_development_dependency 'climate_control', '>= 0.2.0' From 81d71b8a13d4e10d56dd8c01d7118cc4f463d26a Mon Sep 17 00:00:00 2001 From: Samuel Beaulieu Date: Tue, 7 Dec 2021 14:54:16 -0600 Subject: [PATCH 05/70] Move vSphere specific methods out of VMPooler VMPooler has the vSphere provider taken out, moving some vSphere related methods to the provider: 1) pool_folders 2) get_base_folders And the related spec tests. At the same time renaming some configuration and code items to remove harmful terminology. Note this version of the vsphere provider needs to run on vmpooler that also contain the renaming changes (version >2.1) --- Gemfile.lock | 6 +- lib/vmpooler/providers/vsphere.rb | 31 +++++++-- spec/unit/pool_manager_spec.rb | 102 ++++++---------------------- spec/unit/providers/vsphere_spec.rb | 84 ++++++++++++++++++----- vmpooler-provider-vsphere.gemspec | 2 +- 5 files changed, 118 insertions(+), 107 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5bde0f7..23c1771 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -136,7 +136,7 @@ GEM rubocop-ast (>= 1.0.1) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.14.0) + rubocop-ast (1.15.0) parser (>= 3.0.1.1) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) @@ -160,7 +160,7 @@ GEM thrift (0.15.0) tilt (2.0.10) unicode-display_width (1.8.0) - vmpooler (2.0.0) + vmpooler (2.1.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) net-ldap (~> 0.16) @@ -197,7 +197,7 @@ DEPENDENCIES rubocop (~> 1.1.0) simplecov (>= 0.11.2) thor (~> 1.0, >= 1.0.1) - vmpooler (~> 2.0) + vmpooler (~> 2.1) vmpooler-provider-vsphere! yarjuf (>= 2.0) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index b860743..c572d48 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -51,8 +51,8 @@ module Vmpooler 'vsphere' end - def folder_configured?(folder_title, base_folder, configured_folders, whitelist) - return true if whitelist&.include?(folder_title) + def folder_configured?(folder_title, base_folder, configured_folders, allowlist) + return true if allowlist&.include?(folder_title) return false unless configured_folders.keys.include?(folder_title) return false unless configured_folders[folder_title] == base_folder @@ -119,7 +119,30 @@ module Vmpooler try >= max_tries ? raise : retry end - def purge_unconfigured_folders(base_folders, configured_folders, whitelist) + # Return a list of pool folders + def pool_folders(provider_name) + folders = {} + $config[:pools].each do |pool| + next unless pool['provider'] == provider_name.to_s + + folder_parts = pool['folder'].split('/') + datacenter = get_target_datacenter_from_config(pool['name']) + folders[folder_parts.pop] = "#{datacenter}/vm/#{folder_parts.join('/')}" + end + folders + end + + def get_base_folders(folders) + base = [] + folders.each do |_key, value| + base << value + end + base.uniq + end + + def purge_unconfigured_resources(allowlist) + configured_folders = pool_folders(name) + base_folders = get_base_folders(configured_folders) @connection_pool.with_metrics do |pool_object| connection = ensured_vsphere_connection(pool_object) @@ -129,7 +152,7 @@ module Vmpooler folder_children.each do |folder_hash| folder_hash.each do |folder_title, folder_object| - destroy_folder_and_children(folder_object) unless folder_configured?(folder_title, base_folder, configured_folders, whitelist) + destroy_folder_and_children(folder_object) unless folder_configured?(folder_title, base_folder, configured_folders, allowlist) end end end diff --git a/spec/unit/pool_manager_spec.rb b/spec/unit/pool_manager_spec.rb index 24dae84..4f551eb 100644 --- a/spec/unit/pool_manager_spec.rb +++ b/spec/unit/pool_manager_spec.rb @@ -61,7 +61,7 @@ EOT --- :config: :providers: - :mock: + :vsphere: :pools: - name: '#{pool}' size: 1 @@ -1060,7 +1060,7 @@ EOT end end - describe '#purge_unused_vms_and_folders' do + describe '#purge_unused_vms_and_resources' do let(:config) { YAML.load(<<-EOT --- :config: {} @@ -1074,104 +1074,46 @@ EOT } it 'should return when purging is not enabled' do - expect(subject.purge_unused_vms_and_folders).to be_nil + expect(subject.purge_unused_vms_and_resources).to be_nil end context 'with purging enabled globally' do before(:each) do - config[:config]['purge_unconfigured_folders'] = true + config[:config]['purge_unconfigured_resources'] = true expect(Thread).to receive(:new).and_yield end it 'should run a purge for each provider' do - expect(subject).to receive(:purge_vms_and_folders) + expect(subject).to receive(:purge_vms_and_resources) - subject.purge_unused_vms_and_folders + subject.purge_unused_vms_and_resources end it 'should log when purging fails' do - expect(subject).to receive(:purge_vms_and_folders).and_raise(RuntimeError,'MockError') + expect(subject).to receive(:purge_vms_and_resources).and_raise(RuntimeError,'MockError') expect(logger).to receive(:log).with('s', '[!] failed while purging provider mock VMs and folders with an error: MockError') - subject.purge_unused_vms_and_folders + subject.purge_unused_vms_and_resources end end context 'with purging enabled on the provider' do before(:each) do - config[:providers][:mock]['purge_unconfigured_folders'] = true + config[:providers][:mock]['purge_unconfigured_resources'] = true expect(Thread).to receive(:new).and_yield end it 'should run a purge for the provider' do - expect(subject).to receive(:purge_vms_and_folders) + expect(subject).to receive(:purge_vms_and_resources) - subject.purge_unused_vms_and_folders + subject.purge_unused_vms_and_resources end end end - describe '#pool_folders' do - let(:folder_name) { 'myinstance' } - let(:folder_base) { 'vmpooler' } - let(:folder) { [folder_base,folder_name].join('/') } - let(:datacenter) { 'dc1' } + describe '#purge_vms_and_resources' do let(:provider_name) { 'mock_provider' } - let(:expected_response) { - { - folder_name => "#{datacenter}/vm/#{folder_base}" - } - } - let(:config) { YAML.load(<<-EOT ---- -:providers: - :mock: -:pools: - - name: '#{pool}' - folder: '#{folder}' - size: 1 - datacenter: '#{datacenter}' - provider: '#{provider_name}' - - name: '#{pool}2' - folder: '#{folder}' - size: 1 - datacenter: '#{datacenter}' - provider: '#{provider_name}2' -EOT - ) - } - - context 'when evaluating pool folders' do - before do - expect(subject).not_to be_nil - # Inject mock provider into global variable - Note this is a code smell - $providers = { provider_name => provider } - end - - it 'should return a list of pool folders' do - expect(provider).to receive(:get_target_datacenter_from_config).with(pool).and_return(datacenter) - - expect(subject.pool_folders(provider_name)).to eq(expected_response) - end - - it 'should raise an error when the provider fails to get the datacenter' do - expect(provider).to receive(:get_target_datacenter_from_config).with(pool).and_raise('mockerror') - - expect{ subject.pool_folders(provider_name) }.to raise_error(RuntimeError, 'mockerror') - end - end - end - - describe '#purge_vms_and_folders' do - let(:folder_name) { 'myinstance' } - let(:folder_base) { 'vmpooler' } - let(:datacenter) { 'dc1' } - let(:full_folder_path) { "#{datacenter}/vm/folder_base" } - let(:configured_folders) { { folder_name => full_folder_path } } - let(:base_folders) { [ full_folder_path ] } - let(:folder) { [folder_base,folder_name].join('/') } - let(:provider_name) { 'mock_provider' } - let(:whitelist) { nil } + let(:allowlist) { nil } let(:config) { YAML.load(<<-EOT --- :config: {} @@ -1179,9 +1121,7 @@ EOT :mock_provider: {} :pools: - name: '#{pool}' - folder: '#{folder}' size: 1 - datacenter: '#{datacenter}' provider: '#{provider_name}' EOT ) @@ -1194,20 +1134,18 @@ EOT $providers = { provider_name => provider } end - it 'should run purge_unconfigured_folders' do - expect(subject).to receive(:pool_folders).and_return(configured_folders) - expect(provider).to receive(:purge_unconfigured_folders).with(base_folders, configured_folders, whitelist) - expect(provider).to receive(:provider_config).and_return({}) + it 'should run purge_unconfigured_resources' do + expect(provider).to receive(:purge_unconfigured_resources).with(allowlist) + allow(provider).to receive(:provider_config).and_return({}) - subject.purge_vms_and_folders(provider_name) + subject.purge_vms_and_resources(provider_name) end it 'should raise any errors' do - expect(subject).to receive(:pool_folders).and_return(configured_folders) - expect(provider).to receive(:purge_unconfigured_folders).with(base_folders, configured_folders, whitelist).and_raise('mockerror') - expect(provider).to receive(:provider_config).and_return({}) + expect(provider).to receive(:purge_unconfigured_resources).with(allowlist).and_raise('mockerror') + allow(provider).to receive(:provider_config).and_return({}) - expect{ subject.purge_vms_and_folders(provider_name) }.to raise_error(RuntimeError, 'mockerror') + expect{ subject.purge_vms_and_resources(provider_name) }.to raise_error(RuntimeError, 'mockerror') end end end diff --git a/spec/unit/providers/vsphere_spec.rb b/spec/unit/providers/vsphere_spec.rb index b05457c..f2a1975 100644 --- a/spec/unit/providers/vsphere_spec.rb +++ b/spec/unit/providers/vsphere_spec.rb @@ -104,36 +104,36 @@ EOT let(:other_folder) { 'folder2' } let(:base_folder) { 'dc1/vm/base' } let(:configured_folders) { { folder_title => base_folder } } - let(:whitelist) { nil } + let(:allowlist) { nil } it 'should return true when configured_folders includes the folder_title' do - expect(subject.folder_configured?(folder_title, base_folder, configured_folders, whitelist)).to be true + expect(subject.folder_configured?(folder_title, base_folder, configured_folders, allowlist)).to be true end it 'should return false when title is not in configured_folders' do - expect(subject.folder_configured?(other_folder, base_folder, configured_folders, whitelist)).to be false + expect(subject.folder_configured?(other_folder, base_folder, configured_folders, allowlist)).to be false end context 'with another base folder' do let(:base_folder) { 'dc2/vm/base' } let(:configured_folders) { { folder_title => 'dc1/vm/base' } } it 'should return false' do - expect(subject.folder_configured?(folder_title, base_folder, configured_folders, whitelist)).to be false + expect(subject.folder_configured?(folder_title, base_folder, configured_folders, allowlist)).to be false end end - context 'with a whitelist set' do - let(:whitelist) { [ other_folder ] } + context 'with a allowlist set' do + let(:allowlist) { [ other_folder ] } it 'should return true' do - expect(subject.folder_configured?(other_folder, base_folder, configured_folders, whitelist)).to be true + expect(subject.folder_configured?(other_folder, base_folder, configured_folders, allowlist)).to be true end end - context 'with string whitelist value' do - let(:whitelist) { 'whitelist' } + context 'with string allowlist value' do + let(:allowlist) { 'allowlist' } it 'should raise an error' do - expect(whitelist).to receive(:include?).and_raise('mockerror') + expect(allowlist).to receive(:include?).and_raise('mockerror') - expect{ subject.folder_configured?(other_folder, base_folder, configured_folders, whitelist) }.to raise_error(RuntimeError, 'mockerror') + expect{ subject.folder_configured?(other_folder, base_folder, configured_folders, allowlist) }.to raise_error(RuntimeError, 'mockerror') end end end @@ -297,12 +297,61 @@ EOT end end - describe '#purge_unconfigured_folders' do + describe '#pool_folders' do + let(:pool) { 'pool1' } + let(:folder_name) { 'myinstance' } + let(:folder_base) { 'vmpooler' } + let(:folder) { [folder_base,folder_name].join('/') } + let(:datacenter) { 'dc1' } + let(:provider_name) { 'mock_provider' } + let(:expected_response) { + { + folder_name => "#{datacenter}/vm/#{folder_base}" + } + } + context 'when evaluating pool folders' do + before do + expect(subject).not_to be_nil + #replace top-level global config + $config = YAML.load(<<-EOT +--- +:providers: + :mock: +:pools: + - name: '#{pool}' + folder: '#{folder}' + size: 1 + datacenter: '#{datacenter}' + provider: '#{provider_name}' + - name: '#{pool}2' + folder: '#{folder}' + size: 1 + datacenter: '#{datacenter}' + provider: '#{provider_name}2' + EOT + ) + end + + it 'should return a list of pool folders' do + expect(subject).to receive(:get_target_datacenter_from_config).with(pool).and_return(datacenter) + + expect(subject.pool_folders(provider_name)).to eq(expected_response) + end + + it 'should raise an error when the provider fails to get the datacenter' do + expect(subject).to receive(:get_target_datacenter_from_config).with(pool).and_raise('mockerror') + + expect{ subject.pool_folders(provider_name) }.to raise_error(RuntimeError, 'mockerror') + end + end + end + + describe '#purge_unconfigured_resources' do let(:folder_title) { 'folder1' } let(:base_folder) { 'dc1/vm/base' } let(:folder_object) { mock_RbVmomi_VIM_Folder({ :name => base_folder }) } let(:child_folder) { mock_RbVmomi_VIM_Folder({ :name => folder_title }) } - let(:whitelist) { nil } + let(:allowlist) { nil } let(:base_folders) { [ base_folder ] } let(:configured_folders) { { folder_title => base_folder } } let(:folder_children) { [ folder_title => child_folder ] } @@ -310,6 +359,7 @@ EOT before(:each) do allow(subject).to receive(:connect_to_vsphere).and_return(connection) + allow(subject).to receive(:pool_folders).and_return(configured_folders) end context 'with an empty folder' do @@ -317,7 +367,7 @@ EOT expect(subject).to receive(:get_folder_children).with(base_folder, connection).and_return(empty_list) expect(subject).to_not receive(:destroy_folder_and_children) - subject.purge_unconfigured_folders(base_folders, configured_folders, whitelist) + subject.purge_unconfigured_resources(allowlist) end end @@ -325,7 +375,7 @@ EOT expect(subject).to receive(:get_folder_children).with(base_folder, connection).and_return(folder_children) allow(subject).to receive(:folder_configured?).and_return(true) - subject.purge_unconfigured_folders(base_folders, configured_folders, whitelist) + subject.purge_unconfigured_resources(allowlist) end context 'with a folder that is not configured' do @@ -337,14 +387,14 @@ EOT it 'should destroy the folder and children' do expect(subject).to receive(:destroy_folder_and_children).with(child_folder).and_return(nil) - subject.purge_unconfigured_folders(base_folders, configured_folders, whitelist) + subject.purge_unconfigured_resources(allowlist) end end it 'should raise any errors' do expect(subject).to receive(:get_folder_children).and_throw('mockerror') - expect{ subject.purge_unconfigured_folders(base_folders, configured_folders, whitelist) }.to raise_error(/mockerror/) + expect{ subject.purge_unconfigured_resources(allowlist) }.to raise_error(/mockerror/) end end diff --git a/vmpooler-provider-vsphere.gemspec b/vmpooler-provider-vsphere.gemspec index a0d332b..6c7febe 100644 --- a/vmpooler-provider-vsphere.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.require_paths = ["lib"] s.add_dependency 'rbvmomi', '>= 2.1', '< 4.0' - s.add_development_dependency 'vmpooler', '~> 2.0' + s.add_development_dependency 'vmpooler', '~> 2.1' # renaming done in version 2.1 # Testing dependencies s.add_development_dependency 'climate_control', '>= 0.2.0' From 6f76c536ea41bec7262e0d0be8f5a7afad043d0f Mon Sep 17 00:00:00 2001 From: Gene Liverman Date: Mon, 13 Dec 2021 10:38:53 -0500 Subject: [PATCH 06/70] Bump version to 1.5.0, require vmpooler >= 2.1 --- Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 23c1771..6811abd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (1.4.0) + vmpooler-provider-vsphere (1.5.0) rbvmomi (>= 2.1, < 4.0) GEM diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index 234751f..ec8c193 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '1.4.0' + VERSION = '1.5.0' end From 6f641e2f2300b5989d822c3b8c3ca00918071a78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Apr 2022 13:29:08 +0000 Subject: [PATCH 07/70] Update rubocop requirement from ~> 1.1.0 to ~> 1.28.2 Updates the requirements on [rubocop](https://github.com/rubocop/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.1.0...v1.28.2) --- updated-dependencies: - dependency-name: rubocop dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 26 +++++++++++++------------- vmpooler-provider-vsphere.gemspec | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6811abd..4ddf540 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -81,8 +81,8 @@ GEM opentelemetry-common (~> 0.17.0) opentelemetry-instrumentation-base (~> 0.17.0) optimist (3.0.1) - parallel (1.21.0) - parser (3.0.3.2) + parallel (1.22.1) + parser (3.1.2.0) ast (~> 2.4.1) pickup (0.0.11) prometheus-client (2.1.0) @@ -104,7 +104,7 @@ GEM rack rack-test (1.1.0) rack (>= 1.0, < 3) - rainbow (3.0.0) + rainbow (3.1.1) rake (13.0.6) rbvmomi (3.0.0) builder (~> 3.2) @@ -112,7 +112,7 @@ GEM nokogiri (~> 1.10) optimist (~> 3.0) redis (4.5.1) - regexp_parser (2.2.0) + regexp_parser (2.3.1) rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -127,17 +127,17 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.3) - rubocop (1.1.0) + rubocop (1.28.2) parallel (~> 1.10) - parser (>= 2.7.1.5) + parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8) + regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.0.1) + rubocop-ast (>= 1.17.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.15.0) - parser (>= 3.0.1.1) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.17.0) + parser (>= 3.1.1.0) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) simplecov (0.21.2) @@ -159,7 +159,7 @@ GEM thor (1.1.0) thrift (0.15.0) tilt (2.0.10) - unicode-display_width (1.8.0) + unicode-display_width (2.1.0) vmpooler (2.1.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) @@ -194,7 +194,7 @@ DEPENDENCIES pry rack-test (>= 0.6) rspec (>= 3.2) - rubocop (~> 1.1.0) + rubocop (~> 1.28.2) simplecov (>= 0.11.2) thor (~> 1.0, >= 1.0.1) vmpooler (~> 2.1) diff --git a/vmpooler-provider-vsphere.gemspec b/vmpooler-provider-vsphere.gemspec index 6c7febe..e509fd4 100644 --- a/vmpooler-provider-vsphere.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'pry' s.add_development_dependency 'rack-test', '>= 0.6' s.add_development_dependency 'rspec', '>= 3.2' - s.add_development_dependency 'rubocop', '~> 1.1.0' + s.add_development_dependency 'rubocop', '~> 1.28.2' s.add_development_dependency 'simplecov', '>= 0.11.2' s.add_development_dependency 'thor', '~> 1.0', '>= 1.0.1' s.add_development_dependency 'yarjuf', '>= 2.0' From 70e2b03cdec13a255b343365e85238fd80b9554a Mon Sep 17 00:00:00 2001 From: Samuel Beaulieu Date: Mon, 25 Jul 2022 13:00:26 -0500 Subject: [PATCH 08/70] pin to vmpooler 2.4 --- Gemfile.lock | 197 +++++++++++------------ lib/vmpooler-provider-vsphere/version.rb | 2 +- vmpooler-provider-vsphere.gemspec | 2 +- 3 files changed, 93 insertions(+), 108 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6811abd..ba32425 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,123 +10,108 @@ GEM ast (2.4.2) bindata (2.4.10) builder (3.2.4) - climate_control (1.0.1) + climate_control (1.2.0) coderay (1.1.3) - concurrent-ruby (1.1.9) + concurrent-ruby (1.1.10) connection_pool (2.2.5) - diff-lcs (1.4.4) + deep_merge (1.2.2) + diff-lcs (1.5.0) docile (1.4.0) - faraday (1.8.0) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - multipart-post (>= 1.2, < 3) + faraday (2.3.0) + faraday-net_http (~> 2.0) ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - ffi (1.15.4-java) - google-cloud-env (1.5.0) - faraday (>= 0.17.3, < 2.0) - json (2.6.1) - json (2.6.1-java) + faraday-net_http (2.0.3) + ffi (1.15.5-java) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + json (2.6.2-java) method_source (1.0.0) - mock_redis (0.29.0) + mock_redis (0.32.0) ruby2_keywords - multipart-post (2.1.1) - mustermann (1.1.1) + mustermann (2.0.2) ruby2_keywords (~> 0.0.1) - net-ldap (0.17.0) - nio4r (2.5.8) + net-ldap (0.17.1) nio4r (2.5.8-java) - nokogiri (1.12.5-java) + nokogiri (1.13.8-java) racc (~> 1.4) - nokogiri (1.12.5-x86_64-linux) - racc (~> 1.4) - opentelemetry-api (0.17.0) - opentelemetry-common (0.17.0) - opentelemetry-api (~> 0.17.0) - opentelemetry-exporter-jaeger (0.17.0) - opentelemetry-api (~> 0.17.0) - opentelemetry-common (~> 0.17.0) - opentelemetry-sdk (~> 0.17.0) + opentelemetry-api (1.0.2) + opentelemetry-common (0.19.6) + opentelemetry-api (~> 1.0) + opentelemetry-exporter-jaeger (0.20.1) + opentelemetry-api (~> 1.0) + opentelemetry-common (~> 0.19.2) + opentelemetry-sdk (~> 1.0) thrift - opentelemetry-instrumentation-base (0.17.0) - opentelemetry-api (~> 0.17.0) - opentelemetry-instrumentation-concurrent_ruby (0.17.0) - opentelemetry-api (~> 0.17.0) - opentelemetry-instrumentation-base (~> 0.17.0) - opentelemetry-instrumentation-redis (0.17.0) - opentelemetry-api (~> 0.17.0) - opentelemetry-common (~> 0.17.0) - opentelemetry-instrumentation-base (~> 0.17.0) - opentelemetry-instrumentation-sinatra (0.17.0) - opentelemetry-api (~> 0.17.0) - opentelemetry-instrumentation-base (~> 0.17.0) - opentelemetry-resource_detectors (0.17.0) + opentelemetry-instrumentation-base (0.19.0) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-concurrent_ruby (0.19.2) + opentelemetry-api (~> 1.0) + opentelemetry-instrumentation-base (~> 0.19.0) + opentelemetry-instrumentation-http_client (0.19.4) + opentelemetry-api (~> 1.0) + opentelemetry-common (~> 0.19.3) + opentelemetry-instrumentation-base (~> 0.19.0) + opentelemetry-instrumentation-redis (0.21.3) + opentelemetry-api (~> 1.0) + opentelemetry-common (~> 0.19.3) + opentelemetry-instrumentation-base (~> 0.19.0) + opentelemetry-instrumentation-sinatra (0.19.3) + opentelemetry-api (~> 1.0) + opentelemetry-common (~> 0.19.3) + opentelemetry-instrumentation-base (~> 0.19.0) + opentelemetry-registry (0.1.0) + opentelemetry-api (~> 1.0.1) + opentelemetry-resource_detectors (0.19.1) google-cloud-env opentelemetry-sdk - opentelemetry-sdk (0.17.0) - opentelemetry-api (~> 0.17.0) - opentelemetry-common (~> 0.17.0) - opentelemetry-instrumentation-base (~> 0.17.0) + opentelemetry-sdk (1.1.0) + opentelemetry-api (~> 1.0) + opentelemetry-common (~> 0.19.3) + opentelemetry-registry (~> 0.1) + opentelemetry-semantic_conventions + opentelemetry-semantic_conventions (1.8.0) + opentelemetry-api (~> 1.0) optimist (3.0.1) - parallel (1.21.0) - parser (3.0.3.2) + parallel (1.22.1) + parser (3.1.2.0) ast (~> 2.4.1) pickup (0.0.11) prometheus-client (2.1.0) - pry (0.14.1) - coderay (~> 1.1) - method_source (~> 1.0) pry (0.14.1-java) coderay (~> 1.1) method_source (~> 1.0) spoon (~> 0.0) - puma (5.5.2) + puma (5.6.4-java) nio4r (~> 2.0) - puma (5.5.2-java) - nio4r (~> 2.0) - racc (1.6.0) racc (1.6.0-java) - rack (2.2.3) - rack-protection (2.1.0) + rack (2.2.4) + rack-protection (2.2.2) rack - rack-test (1.1.0) - rack (>= 1.0, < 3) - rainbow (3.0.0) + rack-test (2.0.2) + rack (>= 1.3) + rainbow (3.1.1) rake (13.0.6) rbvmomi (3.0.0) builder (~> 3.2) json (~> 2.3) nokogiri (~> 1.10) optimist (~> 3.0) - redis (4.5.1) - regexp_parser (2.2.0) + redis (4.7.1) + regexp_parser (2.5.0) rexml (3.2.5) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.3) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) rubocop (1.1.0) parallel (~> 1.10) parser (>= 2.7.1.5) @@ -136,8 +121,8 @@ GEM rubocop-ast (>= 1.0.1) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.15.0) - parser (>= 3.0.1.1) + rubocop-ast (1.19.1) + parser (>= 3.1.1.0) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) simplecov (0.21.2) @@ -145,32 +130,33 @@ GEM simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) - simplecov_json_formatter (0.1.3) - sinatra (2.1.0) - mustermann (~> 1.0) + simplecov_json_formatter (0.1.4) + sinatra (2.2.2) + mustermann (~> 2.0) rack (~> 2.2) - rack-protection (= 2.1.0) + rack-protection (= 2.2.2) tilt (~> 2.0) - spicy-proton (2.1.13) + spicy-proton (2.1.14) bindata (~> 2.3) spoon (0.0.6) ffi statsd-ruby (1.5.0) - thor (1.1.0) - thrift (0.15.0) - tilt (2.0.10) + thor (1.2.1) + thrift (0.16.0) + tilt (2.0.11) unicode-display_width (1.8.0) - vmpooler (2.1.0) + vmpooler (2.4.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) + deep_merge (~> 1.2) net-ldap (~> 0.16) - nokogiri (~> 1.10) - opentelemetry-exporter-jaeger (= 0.17.0) - opentelemetry-instrumentation-concurrent_ruby (= 0.17.0) - opentelemetry-instrumentation-redis (= 0.17.0) - opentelemetry-instrumentation-sinatra (= 0.17.0) - opentelemetry-resource_detectors (= 0.17.0) - opentelemetry-sdk (= 0.17.0) + opentelemetry-exporter-jaeger (= 0.20.1) + opentelemetry-instrumentation-concurrent_ruby (= 0.19.2) + opentelemetry-instrumentation-http_client (= 0.19.4) + opentelemetry-instrumentation-redis (= 0.21.3) + opentelemetry-instrumentation-sinatra (= 0.19.3) + opentelemetry-resource_detectors (= 0.19.1) + opentelemetry-sdk (~> 1.0, >= 1.0.2) pickup (~> 0.0.11) prometheus-client (~> 2.0) puma (~> 5.0, >= 5.0.4) @@ -186,7 +172,6 @@ GEM PLATFORMS universal-java-11 - x86_64-linux DEPENDENCIES climate_control (>= 0.2.0) @@ -197,9 +182,9 @@ DEPENDENCIES rubocop (~> 1.1.0) simplecov (>= 0.11.2) thor (~> 1.0, >= 1.0.1) - vmpooler (~> 2.1) + vmpooler (~> 2.4) vmpooler-provider-vsphere! yarjuf (>= 2.0) BUNDLED WITH - 2.2.22 + 2.2.29 diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index ec8c193..e8ab665 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '1.5.0' + VERSION = '1.6.0' end diff --git a/vmpooler-provider-vsphere.gemspec b/vmpooler-provider-vsphere.gemspec index 6c7febe..4c66499 100644 --- a/vmpooler-provider-vsphere.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.require_paths = ["lib"] s.add_dependency 'rbvmomi', '>= 2.1', '< 4.0' - s.add_development_dependency 'vmpooler', '~> 2.1' # renaming done in version 2.1 + s.add_development_dependency 'vmpooler', '~> 2.4' # renaming done in version 2.1 # Testing dependencies s.add_development_dependency 'climate_control', '>= 0.2.0' From 122fc687760330621ba72a97ab1ed831b604c4ac Mon Sep 17 00:00:00 2001 From: Samuel Beaulieu Date: Mon, 25 Jul 2022 13:50:07 -0500 Subject: [PATCH 09/70] also update github workflows --- .github/workflows/release.yml | 4 ++-- .github/workflows/testing.yml | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac38a53..71bb52a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,10 +20,10 @@ jobs: draft: false prerelease: false generateReleaseNotes: true - - name: Install Ruby 2.5.8 + - name: Install Ruby jruby-9.3.6.0 uses: ruby/setup-ruby@v1 with: - ruby-version: '2.5.8' + ruby-version: 'jruby-9.3.6.0' - name: Build gem run: gem build *.gemspec - name: Publish gem diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 501403f..068495e 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: ruby-version: - - '2.5.8' + - 'jruby-9.3.6.0' steps: - uses: actions/checkout@v2 - name: Set up Ruby @@ -34,8 +34,7 @@ jobs: strategy: matrix: ruby-version: - - '2.5.8' - - 'jruby-9.2.12.0' + - 'jruby-9.3.6.0' steps: - uses: actions/checkout@v2 - name: Set up Ruby From fe62c4987ac246a23477b5240b4885a9926ec9fd Mon Sep 17 00:00:00 2001 From: Samuel Beaulieu Date: Mon, 25 Jul 2022 13:52:28 -0500 Subject: [PATCH 10/70] refresh Gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index ba32425..eb102a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (1.5.0) + vmpooler-provider-vsphere (1.6.0) rbvmomi (>= 2.1, < 4.0) GEM From 7c096dd3a2dd4c1da6e3642ab4b00c9ed05fa072 Mon Sep 17 00:00:00 2001 From: Samuel Beaulieu Date: Mon, 25 Jul 2022 14:47:51 -0500 Subject: [PATCH 11/70] remove upstream spec tests removed spec tests that did not exercise vsphere code paths --- spec/unit/pool_manager_spec.rb | 656 --------------------------------- 1 file changed, 656 deletions(-) diff --git a/spec/unit/pool_manager_spec.rb b/spec/unit/pool_manager_spec.rb index 4f551eb..0b14668 100644 --- a/spec/unit/pool_manager_spec.rb +++ b/spec/unit/pool_manager_spec.rb @@ -235,180 +235,6 @@ EOT end end - describe '#move_pending_vm_to_ready' do - let(:host) { { 'hostname' => vm }} - - before do - expect(subject).not_to be_nil - end - - before(:each) do - redis_connection_pool.with do |redis| - create_pending_vm(pool,vm,redis) - end - end - - context 'when hostname matches VM name' do - it 'should move the VM from pending to ready pool' do - redis_connection_pool.with do |redis| - expect(redis.sismember("vmpooler__pending__#{pool}", vm)).to be(true) - expect(redis.sismember("vmpooler__ready__#{pool}", vm)).to be(false) - subject.move_pending_vm_to_ready(vm, pool, redis) - expect(redis.sismember("vmpooler__pending__#{pool}", vm)).to be(false) - expect(redis.sismember("vmpooler__ready__#{pool}", vm)).to be(true) - end - end - - it 'should log a message' do - expect(logger).to receive(:log).with('s', "[>] [#{pool}] '#{vm}' moved from 'pending' to 'ready' queue") - - redis_connection_pool.with do |redis| - subject.move_pending_vm_to_ready(vm, pool, redis) - end - end - - it 'should receive time_to_ready_state metric' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone',Time.now.to_s) - expect(metrics).to receive(:timing).with(/time_to_ready_state\./,/0/) - - subject.move_pending_vm_to_ready(vm, pool, redis) - end - end - - - it 'should set the boot time in redis' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone',Time.now.to_s) - expect(redis.hget('vmpooler__boot__' + Date.today.to_s, pool + ':' + vm)).to be_nil - subject.move_pending_vm_to_ready(vm, pool, redis) - expect(redis.hget('vmpooler__boot__' + Date.today.to_s, pool + ':' + vm)).to_not be_nil - # TODO Should we inspect the value to see if it's valid? - end - end - - it 'should not determine boot timespan if clone start time not set' do - redis_connection_pool.with do |redis| - expect(redis.hget('vmpooler__boot__' + Date.today.to_s, pool + ':' + vm)).to be_nil - subject.move_pending_vm_to_ready(vm, pool, redis) - expect(redis.hget('vmpooler__boot__' + Date.today.to_s, pool + ':' + vm)).to match(/\d\.\d{2}/) - end - end - - it 'should raise error if clone start time is not parsable' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone','iamnotparsable_asdate') - expect{subject.move_pending_vm_to_ready(vm, pool, redis)}.to raise_error(/iamnotparsable_asdate/) - end - end - - it 'should save the last boot time' do - redis_connection_pool.with do |redis| - expect(redis.hget('vmpooler__lastboot', pool)).to be(nil) - subject.move_pending_vm_to_ready(vm, pool, redis) - expect(redis.hget('vmpooler__lastboot', pool)).to_not be(nil) - end - end - end - - context 'with request_id' do - context 'with a pending request' do - before(:each) do - allow(subject).to receive(:check_ondemand_request_ready) - config[:config]['ondemand_request_ttl'] = 20 - end - - it 'sets the vm as active' do - redis_connection_pool.with do |redis| - expect(Time).to receive(:now).and_return(current_time).at_least(:once) - redis.hset("vmpooler__vm__#{vm}", 'pool_alias', pool) - subject.move_pending_vm_to_ready(vm, pool, redis, request_id) - expect(redis.hget("vmpooler__active__#{pool}", vm)).to eq(current_time.to_s) - expect(redis.hget("vmpooler__vm__#{vm}", 'checkout')).to eq(current_time.to_s) - expect(redis.sismember("vmpooler__#{request_id}__#{pool}__#{pool}", vm)).to be true - end - end - - it 'logs that the vm is ready for the request' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'pool_alias', pool) - expect(logger).to receive(:log).with('s', "[>] [#{pool}] '#{vm}' is 'ready' for request '#{request_id}'") - - subject.move_pending_vm_to_ready(vm, pool, redis, request_id) - end - end - end - - context 'when the request has been marked as failed' do - before(:each) do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__odrequest__#{request_id}", 'status', 'failed') - end - end - - it 'moves the vm to completed' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:move_vm_queue).with(pool, vm, 'pending', 'completed', redis, "moved to completed queue. '#{request_id}' could not be filled in time") - subject.move_pending_vm_to_ready(vm, pool, redis, request_id) - end - end - - it 'returns nil' do - redis_connection_pool.with do |redis| - result = subject.move_pending_vm_to_ready(vm, pool, redis, request_id) - expect(result).to be nil - end - end - end - - context 'when the request has been marked as deleted' do - before(:each) do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__odrequest__#{request_id}", 'status', 'deleted') - end - end - - it 'moves the vm to completed' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:move_vm_queue).with(pool, vm, 'pending', 'completed', redis, "moved to completed queue. '#{request_id}' has been deleted") - subject.move_pending_vm_to_ready(vm, pool, redis, request_id) - end - end - - it 'returns nil' do - redis_connection_pool.with do |redis| - result = subject.move_pending_vm_to_ready(vm, pool, redis, request_id) - expect(result).to be nil - end - end - end - - context 'with auth on the request' do - let(:user) { 'vmpuser' } - let(:platform_alias) { pool } - let(:platforms_string) { "#{platform_alias}:#{pool}:1" } - let(:score) { current_time.to_i } - before(:each) do - config[:config]['ondemand_request_ttl'] = 20 - allow(subject).to receive(:check_ondemand_request_ready) - redis_connection_pool.with do |redis| - create_ondemand_request_for_test(request_id, score, platforms_string, redis, user, token) - end - end - - it 'should specify auth data on the vm' do - redis_connection_pool.with do |redis| - allow(redis).to receive(:hset) - expect(redis).to receive(:hset).with("vmpooler__vm__#{vm}", 'token:token', token) - expect(redis).to receive(:hset).with("vmpooler__vm__#{vm}", 'token:user', user) - - subject.move_pending_vm_to_ready(vm, pool, redis, request_id) - end - end - end - end - end - describe '#check_ready_vm' do let(:ttl) { 5 } let(:poolconfig) { config[:pools][0] } @@ -771,168 +597,6 @@ EOT end end - describe '#_clone_vm' do - let (:pool_object) { { 'name' => pool } } - let (:redis_ttl) { 1 } - - before do - expect(subject).not_to be_nil - end - - let(:config) { - YAML.load(<<-EOT ---- -:config: - prefix: "prefix" -:redis: - ttl: #{redis_ttl} -EOT - ) - } - - context 'with no errors during cloning' do - before(:each) do - allow(metrics).to receive(:timing) - expect(metrics).to receive(:timing).with(/clone\./,/0/) - expect(provider).to receive(:create_vm).with(pool, String) - allow(logger).to receive(:log) - redis_connection_pool.with do |redis| - expect(subject).to receive(:find_unique_hostname).with(pool).and_return(vm) - end - end - - it 'should create a cloning VM' do - redis_connection_pool.with do |redis| - expect(redis.scard("vmpooler__pending__#{pool}")).to eq(0) - - subject._clone_vm(pool,provider) - - expect(redis.scard("vmpooler__pending__#{pool}")).to eq(1) - expect(redis.hget("vmpooler__vm__#{vm}", 'clone')).to_not be_nil - expect(redis.hget("vmpooler__vm__#{vm}", 'template')).to eq(pool) - expect(redis.hget("vmpooler__clone__#{Date.today.to_s}", "#{pool}:#{vm}")).to_not be_nil - expect(redis.hget("vmpooler__vm__#{vm}", 'clone_time')).to_not be_nil - end - end - - it 'should decrement the clone tasks counter' do - redis_connection_pool.with do |redis| - redis.incr('vmpooler__tasks__clone') - redis.incr('vmpooler__tasks__clone') - expect(redis.get('vmpooler__tasks__clone')).to eq('2') - subject._clone_vm(pool,provider) - expect(redis.get('vmpooler__tasks__clone')).to eq('1') - end - end - - it 'should log a message that is being cloned from a template' do - expect(logger).to receive(:log).with('d',/\[ \] \[#{pool}\] Starting to clone '(.+)'/) - - subject._clone_vm(pool,provider) - end - - it 'should log a message that it completed being cloned' do - expect(logger).to receive(:log).with('s',/\[\+\] \[#{pool}\] '(.+)' cloned in [0-9.]+ seconds/) - - subject._clone_vm(pool,provider) - end - end - - context 'with an error during cloning' do - before(:each) do - expect(provider).to receive(:create_vm).with(pool, String).and_raise('MockError') - allow(logger).to receive(:log) - redis_connection_pool.with do |redis| - expect(subject).to receive(:find_unique_hostname).with(pool).and_return(vm) - end - end - - it 'should not create a cloning VM' do - redis_connection_pool.with do |redis| - expect(redis.scard("vmpooler__pending__#{pool}")).to eq(0) - - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) - - expect(redis.scard("vmpooler__pending__#{pool}")).to eq(0) - # Get the new VM Name from the pending pool queue as it should be the only entry - vm_name = redis.smembers("vmpooler__pending__#{pool}")[0] - expect(vm_name).to be_nil - end - end - - it 'should decrement the clone tasks counter' do - redis_connection_pool.with do |redis| - redis.incr('vmpooler__tasks__clone') - redis.incr('vmpooler__tasks__clone') - expect(redis.get('vmpooler__tasks__clone')).to eq('2') - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) - expect(redis.get('vmpooler__tasks__clone')).to eq('1') - end - end - - it 'should expire the vm metadata' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:expire) - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) - end - end - - it 'should raise the error' do - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) - end - - end - - context 'with request_id' do - before(:each) do - allow(metrics).to receive(:timing) - expect(metrics).to receive(:timing).with(/clone\./,/0/) - expect(provider).to receive(:create_vm).with(pool, String) - allow(logger).to receive(:log) - redis_connection_pool.with do |redis| - expect(subject).to receive(:find_unique_hostname).with(pool).and_return(vm) - end - end - - it 'should set request_id and pool_alias on the vm data' do - redis_connection_pool.with do |redis| - subject._clone_vm(pool,provider,request_id,pool) - expect(redis.hget("vmpooler__vm__#{vm}", 'pool_alias')).to eq(pool) - expect(redis.hget("vmpooler__vm__#{vm}", 'request_id')).to eq(request_id) - end - end - - it 'should reduce the clone count' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:decr).with('vmpooler__tasks__ondemandclone') - subject._clone_vm(pool,provider,request_id,pool) - end - end - end - - context 'with #check_dns_available' do - before(:each) do - allow(logger).to receive(:log) - end - it 'should error out if DNS already exists' do - vm_name = "foo" - resolv = class_double("Resolv").as_stubbed_const(:transfer_nested_constants => true) - expect(subject).to receive(:generate_and_check_hostname).exactly(3).times.and_return([vm_name, true]) #skip this, make it available all times - expect(resolv).to receive(:getaddress).exactly(3).times.and_return("1.2.3.4") - expect(metrics).to receive(:increment).with("errors.staledns.#{pool}").exactly(3).times - expect{subject._clone_vm(pool,provider)}.to raise_error(/Unable to generate a unique hostname after/) - end - it 'should be successful if DNS does not exist' do - vm_name = "foo" - resolv = class_double("Resolv").as_stubbed_const(:transfer_nested_constants => true) - expect(subject).to receive(:generate_and_check_hostname).and_return([vm_name, true]) - expect(resolv).to receive(:getaddress).exactly(1).times.and_raise(Resolv::ResolvError) - expect(provider).to receive(:create_vm).with(pool, String) - subject._clone_vm(pool,provider) - end - end - end - describe '#destroy_vm' do before do expect(subject).not_to be_nil @@ -954,112 +618,6 @@ EOT end end - describe "#_destroy_vm" do - before(:each) do - expect(subject).not_to be_nil - - redis_connection_pool.with do |redis| - create_completed_vm(vm,pool,redis,true) - end - - allow(provider).to receive(:destroy_vm).with(pool,vm).and_return(true) - - # Set redis configuration - config[:redis] = {} - config[:redis]['data_ttl'] = 168 - end - - context 'when redis data_ttl is not specified in the configuration' do - before(:each) do - config[:redis]['data_ttl'] = nil - end - - it 'should call redis expire with 0' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:expire).with("vmpooler__vm__#{vm}", 0) - subject._destroy_vm(vm,pool,provider) - end - end - end - - context 'when there is no redis section in the configuration' do - before(:each) do - config[:redis] = nil - end - - it 'should raise an error' do - expect{ subject._destroy_vm(vm,pool,provider) }.to raise_error(NoMethodError) - end - end - - context 'when a VM does not exist' do - before(:each) do - # As per base_spec, destroy_vm will return true if the VM does not exist - expect(provider).to receive(:destroy_vm).with(pool,vm).and_return(true) - end - - it 'should not raise an error' do - subject._destroy_vm(vm,pool,provider) - end - end - - context 'when the VM is destroyed without error' do - it 'should log a message the VM was destroyed' do - expect(logger).to receive(:log).with('s', /\[-\] \[#{pool}\] '#{vm}' destroyed in [0-9.]+ seconds/) - allow(logger).to receive(:log) - - subject._destroy_vm(vm,pool,provider) - end - - it 'should emit a timing metric' do - allow(subject).to receive(:get_vm_usage_labels) - allow(metrics).to receive(:timing) - expect(metrics).to receive(:timing).with("destroy.#{pool}", String) - - subject._destroy_vm(vm,pool,provider) - end - - it 'should dereference the mutex' do - expect(subject).to receive(:dereference_mutex) - - subject._destroy_vm(vm,pool,provider) - end - end - - context 'when the VM destruction raises an eror' do - before(:each) do - # As per base_spec, destroy_vm will return true if the VM does not exist - expect(provider).to receive(:destroy_vm).with(pool,vm).and_raise('MockError') - end - - it 'should not log a message the VM was destroyed' do - expect(logger).to receive(:log).with('s', /\[-\] \[#{pool}\] '#{vm}' destroyed in [0-9.]+ seconds/).exactly(0).times - allow(logger).to receive(:log) - - expect{ subject._destroy_vm(vm,pool,provider) }.to raise_error(/MockError/) - end - - it 'should not emit a timing metric' do - expect(metrics).to receive(:timing).with("destroy.#{pool}", String).exactly(0).times - - expect{ subject._destroy_vm(vm,pool,provider) }.to raise_error(/MockError/) - end - end - - context 'when the VM mutex is locked' do - let(:mutex) { Mutex.new } - before(:each) do - mutex.lock - end - - it 'should return' do - expect(subject).to receive(:vm_mutex).with(vm).and_return(mutex) - - expect(subject._destroy_vm(vm,pool,provider)).to eq(nil) - end - end - end - describe '#purge_unused_vms_and_resources' do let(:config) { YAML.load(<<-EOT --- @@ -4537,120 +4095,6 @@ EOT end end - describe '#create_ondemand_vms' do - context 'when requested does not have corresponding data' do - it 'logs an error' do - redis_connection_pool.with do |redis| - expect(logger).to receive(:log).with('s', "Failed to find odrequest for request_id '1111'") - subject.create_ondemand_vms('1111', redis) - end - end - end - - context 'with a request that has data' do - let(:request_string) { "#{pool}:#{pool}:1" } - before(:each) do - expect(Time).to receive(:now).and_return(current_time).at_least(:once) - redis_connection_pool.with do |redis| - create_ondemand_request_for_test(request_id, current_time.to_i, request_string, redis) - end - end - - it 'creates tasks for instances to be provisioned' do - redis_connection_pool.with do |redis| - allow(redis).to receive(:zadd) - expect(redis).to receive(:zadd).with('vmpooler__odcreate__task', current_time.to_i, "#{request_string}:#{request_id}") - subject.create_ondemand_vms(request_id, redis) - end - end - - it 'adds a member to provisioning__processing' do - redis_connection_pool.with do |redis| - allow(redis).to receive(:zadd) - expect(redis).to receive(:zadd).with('vmpooler__provisioning__processing', current_time.to_i, request_id) - subject.create_ondemand_vms(request_id, redis) - end - end - end - end - - describe '#process_ondemand_vms' do - it 'returns the length of the queue' do - redis_connection_pool.with do |redis| - result = subject.process_ondemand_vms(redis) - expect(result).to eq(0) - end - end - - context 'with a request to create a single vm' do - let(:request_string) { "#{pool}:#{pool}:1" } - let(:pool_alias) { pool } - before(:each) do - config[:config]['ondemand_clone_limit'] = 10 - expect(subject).to receive(:get_provider_for_pool).and_return(provider) - redis_connection_pool.with do |redis| - create_ondemand_creationtask("#{request_string}:#{request_id}", current_time.to_i, redis) - end - end - - it 'creates the vm' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:clone_vm).with(pool, provider, request_id, pool_alias) - subject.process_ondemand_vms(redis) - end - end - end - - context 'with a request to create more instances than the limit' do - let(:request_string) { "#{pool}:#{pool}:5" } - let(:request_string_remaining) { "#{pool}:#{pool}:2" } - let(:pool_alias) { pool } - before(:each) do - config[:config]['ondemand_clone_limit'] = 3 - expect(subject).to receive(:get_provider_for_pool).and_return(provider) - redis_connection_pool.with do |redis| - create_ondemand_creationtask("#{request_string}:#{request_id}", current_time.to_i, redis) - end - end - - it 'should create the maximum number of vms' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:clone_vm).with(pool, provider, request_id, pool_alias).exactly(3).times - subject.process_ondemand_vms(redis) - end - end - - it 'should add the remaining number back as a new create task with the same score' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:zadd).with('vmpooler__odcreate__task', current_time.to_i, "#{request_string_remaining}:#{request_id}") - subject.process_ondemand_vms(redis) - end - end - - it 'should return the number of requests processed' do - redis_connection_pool.with do |redis| - result = subject.process_ondemand_vms(redis) - expect(result).to eq(1) - end - end - end - - context 'when the limit has been reached' do - let(:clone_count) { { 'ondemand_clone_count' => 3 } } - before(:each) do - config[:config]['ondemand_clone_limit'] = 3 - subject.instance_variable_set(:@tasks, clone_count) - end - - it 'does not create any instances' do - redis_connection_pool.with do |redis| - expect(subject).to_not receive(:clone_vm) - subject.process_ondemand_vms(redis) - end - end - end - end - describe '#vms_ready?' do let(:request_string) { "#{pool}:#{pool}:5" } let(:platform_alias) { pool } @@ -4799,106 +4243,6 @@ EOT end end - describe '#request_expired?' do - let(:ondemand_request_ttl) { 5 } - let(:expiration_ttl) { 10 } - before(:each) do - config[:config]['ondemand_request_ttl'] = ondemand_request_ttl - config[:redis]['data_ttl'] = expiration_ttl - end - - context 'with a request that has taken too long to be filled' do - let(:expired_time) { (Time.now - 960).to_i } - before(:each) do - redis_connection_pool.with do |redis| - expect(subject).to receive(:remove_vms_for_failed_request) - create_ondemand_processing(request_id, expired_time, redis) - end - end - - it 'returns true when the request is expired' do - redis_connection_pool.with do |redis| - result = subject.request_expired?(request_id, expired_time, redis) - expect(result).to be true - end - end - - it 'logs a message that the request has expired' do - redis_connection_pool.with do |redis| - expect(logger).to receive(:log).with('s', "Ondemand request for '#{request_id}' failed to provision all instances within the configured ttl '#{ondemand_request_ttl}'") - subject.request_expired?(request_id, expired_time, redis) - end - end - - it 'removes the request from processing requests' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:zrem).with('vmpooler__provisioning__processing', request_id) - subject.request_expired?(request_id, expired_time, redis) - end - end - - it 'sets the status as failed on the request hash' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hset).with("vmpooler__odrequest__#{request_id}", 'status', 'failed') - subject.request_expired?(request_id, expired_time, redis) - end - end - - it 'marks the request hash for expiration' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:expire).with("vmpooler__odrequest__#{request_id}", expiration_ttl * 60 * 60) - subject.request_expired?(request_id, expired_time, redis) - end - end - end - - context 'with a request that has been made within the ttl' do - before(:each) do - redis_connection_pool.with do |redis| - create_ondemand_processing(request_id, current_time, redis) - end - end - - it 'should return false' do - redis_connection_pool.with do |redis| - result = subject.request_expired?(request_id, current_time, redis) - expect(result).to be false - end - end - end - end - - describe '#remove_vms_for_failed_request)' do - let(:expiration_ttl) { 100 * 60 * 60 } - let(:platform_alias) { pool } - let(:platforms_string) { "#{platform_alias}:#{pool}:3" } - context 'with two vms marked as ready for the request' do - before(:each) do - redis_connection_pool.with do |redis| - create_ondemand_request_for_test(request_id, current_time, platforms_string, redis) - [vm,"#{vm}2"].each do |v| - create_running_vm(pool, v, redis) - redis.sadd("vmpooler__#{request_id}__#{platform_alias}__#{pool}", v) - end - end - end - - it 'should remove the associated vms' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:move_vm_queue).with(pool, String, 'running', 'completed', redis, "moved to completed queue. '#{request_id}' could not be filled in time").twice - subject.remove_vms_for_failed_request(request_id, expiration_ttl, redis) - end - end - - it 'should mark the ready set for expiration' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:expire).with("vmpooler__#{request_id}__#{platform_alias}__#{pool}", expiration_ttl) - subject.remove_vms_for_failed_request(request_id, expiration_ttl, redis) - end - end - end - end - describe 'check_ondemand_requests' do let(:threads) {[]} let(:maxloop) { 0 } From b74a1b1146f2b6bf9faea399586dca3b100e8869 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 8 Aug 2022 14:35:56 -0400 Subject: [PATCH 12/70] Add release-engineering to codeowners --- CODEOWNERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 9c4ecfc..282a30d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,10 +1,10 @@ -# This will cause DIO to be assigned review of any opened PRs against +# This will cause DIO and RE to be assigned review of any opened PRs against # the branches containing this file. # See https://help.github.com/en/articles/about-code-owners for info on how to # take ownership of parts of the code base that should be reviewed by another # team. -# DIO will be the default owners for everything in the repo. -* @puppetlabs/dio +# DIO and RE will be the default owners for everything in the repo. +* @puppetlabs/dio @puppetlabs/release-engineering From c0a04b257205d5efbf4313508bbabb289e6a3c39 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Tue, 16 Aug 2022 17:21:43 -0400 Subject: [PATCH 13/70] Add Snyk action --- .github/workflows/snyk.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/snyk.yml diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml new file mode 100644 index 0000000..3d48051 --- /dev/null +++ b/.github/workflows/snyk.yml @@ -0,0 +1,19 @@ +name: Snyk Scan + +on: + workflow_dispatch: + push: + branches: + - main + +jobs: + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/ruby@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_RE_KEY }} + with: + command: monitor From ff61a2e1f63356764573cdc084bcce467e3a1059 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 26 Aug 2022 09:47:20 -0400 Subject: [PATCH 14/70] Remove DIO as codeowners --- CODEOWNERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 282a30d..b47017c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,10 +1,10 @@ -# This will cause DIO and RE to be assigned review of any opened PRs against +# This will cause RE to be assigned review of any opened PRs against # the branches containing this file. # See https://help.github.com/en/articles/about-code-owners for info on how to # take ownership of parts of the code base that should be reviewed by another # team. -# DIO and RE will be the default owners for everything in the repo. -* @puppetlabs/dio @puppetlabs/release-engineering +# RE will be the default owners for everything in the repo. +* @puppetlabs/release-engineering From d2eba9f021eccea086b46fe013397bd5e0241709 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Thu, 19 Jan 2023 21:24:52 -0500 Subject: [PATCH 15/70] Change dependabot to weekly --- .github/dependabot.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c8f8016..81e0069 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,6 +3,5 @@ updates: - package-ecosystem: bundler directory: "/" schedule: - interval: daily - time: "13:00" + interval: weekly open-pull-requests-limit: 10 From 070577e542cf1b31f48a0d13ab6da1d6cc913b0f Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Thu, 19 Jan 2023 21:25:27 -0500 Subject: [PATCH 16/70] Migrate Snyk to Mend Scanning --- .github/workflows/security.yml | 39 ++++++++++++++++++++++++++++++++++ .github/workflows/snyk.yml | 19 ----------------- 2 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/security.yml delete mode 100644 .github/workflows/snyk.yml diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..666c602 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,39 @@ +name: Security +on: + workflow_dispatch: + push: + branches: + - main + +jobs: + scan: + name: Mend Scanning + runs-on: ubuntu-latest + steps: + - name: checkout repo content + uses: actions/checkout@v3 + with: + fetch-depth: 1 + - name: setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + # setup a package lock if one doesn't exist, otherwise do nothing + - name: check lock + run: '[ -f "Gemfile.lock" ] && echo "package lock file exists, skipping" || bundle lock' + # install java + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' # See 'Supported distributions' for available options + java-version: '17' + # download mend + - name: download_mend + run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar + - name: run mend + run: java -jar wss-unified-agent.jar + env: + WS_APIKEY: ${{ secrets.MEND_API_KEY }} + WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent + WS_USERKEY: ${{ secrets.MEND_TOKEN }} + WS_PRODUCTNAME: RE + WS_PROJECTNAME: ${{ github.event.repository.name }} diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml deleted file mode 100644 index 3d48051..0000000 --- a/.github/workflows/snyk.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Snyk Scan - -on: - workflow_dispatch: - push: - branches: - - main - -jobs: - security: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/ruby@master - env: - SNYK_TOKEN: ${{ secrets.SNYK_RE_KEY }} - with: - command: monitor From 0485e2ba615a6282cde422ce8aaa56b1adf15b4b Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 27 Jan 2023 13:55:01 -0500 Subject: [PATCH 17/70] Fix deprecated method of setting output --- .github/workflows/release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 71bb52a..4a682ea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release +name: Release Gem on: workflow_dispatch @@ -11,7 +11,9 @@ jobs: - name: Get Version id: gv run: | - echo "::set-output name=ver::$(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev)" + ver=$(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev) + echo "ver=$ver" >> $GITHUB_OUTPUT + echo "Found version $ver from lib/vmpooler-provider-vsphere/version.rb" - name: Tag Release uses: ncipollo/release-action@v1 with: From c7d24001c54034aa24ec48c97bc0e4fa23df5296 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 27 Jan 2023 14:08:34 -0500 Subject: [PATCH 18/70] Add custom VM attribute information to the docs --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 57bd71e..be6485b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # vmpooler-provider-vsphere +- [vmpooler-provider-vsphere](#vmpooler-provider-vsphere) + - [Usage](#usage) + - [Custom VM Config Attribute](#custom-vm-config-attribute) + - [License](#license) + This is a provider for [VMPooler](https://github.com/puppetlabs/vmpooler) allows using vSphere as a source of machines. This provider was originally part of the main VMPooler code base but was extracted to be a standalone gem so that development could be done independently of VMPooler itself. ## Usage @@ -8,6 +13,15 @@ Include this gem in the same Gemfile that you use to install VMPooler itself and Examples of deploying VMPooler with this provider can be found in the [puppetlabs/vmpooler-deployment](https://github.com/puppetlabs/vmpooler-deployment) repository. +### Custom VM Config Attribute + +This provider sets a custom attribute on the VM called `guestinfo.hostname` to the name of the generated VM, which can be queried from inside the guest OS if VMware Tools is isntalled. For example: + +macOS: `"/Library/Application Support/VMware Tools/vmware-tools-daemon" --cmd "info-get guestinfo.hostname"` +Linux or Windows Guest: `vmtoolsd --cmd "info-get guestinfo.hostname"` + +See the [VMware Tools Administration docs](https://docs.vmware.com/en/VMware-Tools/12.1.0/com.vmware.vsphere.vmwaretools.doc/GUID-D026777B-606D-4442-957A-B953C2049659.html) for more information about querying information from the GuestInfo variable. + ## License vmpooler-provider-vsphere is distributed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). See the [LICENSE](LICENSE) file for more details. From e87b4255736eb95a25491e3fd4a81958b81ad4f3 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 27 Jan 2023 14:22:57 -0500 Subject: [PATCH 19/70] Add changelog generation and release instructions --- .github/workflows/release.yml | 49 ++++++++++++++++++++++++++++++----- .github_changelog_generator | 3 +++ CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++ README.md | 10 +++++++ update-changelog | 5 ++++ 5 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 .github_changelog_generator create mode 100644 CHANGELOG.md create mode 100755 update-changelog diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a682ea..2a1cb5b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,27 +7,64 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'puppetlabs/vmpooler-provider-vsphere' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - name: Get Version id: gv run: | - ver=$(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev) - echo "ver=$ver" >> $GITHUB_OUTPUT - echo "Found version $ver from lib/vmpooler-provider-vsphere/version.rb" + version=$(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev) + echo "version=$version" >> $GITHUB_OUTPUT + echo "Found version $version from lib/vmpooler-provider-vsphere/version.rb" + + - name: Generate Changelog + uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 + with: + args: >- + --future-release ${{ steps.nv.outputs.version }} + env: + CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Validate Changelog + run : | + set -e + if [[ -n $(git status --porcelain) ]]; then + echo "Here is the current git status:" + git status + echo + echo "The following changes were detected:" + git --no-pager diff + echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running 'docker run -it --rm -e CHANGELOG_GITHUB_TOKEN -v "\$\(pwd\)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator:1.16.2 github_changelog_generator --future-release ${{ steps.nv.outputs.version }}'" + exit 1 + fi + + - name: Generate Release Notes + uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 + with: + args: >- + --since-tag ${{ steps.cv.outputs.result }} + --future-release ${{ steps.nv.outputs.version }} + --output release-notes.md + env: + CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Tag Release uses: ncipollo/release-action@v1 with: - tag: ${{ steps.gv.outputs.ver }} + tag: ${{ steps.nv.outputs.version }} token: ${{ secrets.GITHUB_TOKEN }} + bodyfile: release-notes.md draft: false prerelease: false - generateReleaseNotes: true + + # This step should closely match what is used in `docker/Dockerfile` in vmpooler-deployment - name: Install Ruby jruby-9.3.6.0 uses: ruby/setup-ruby@v1 with: ruby-version: 'jruby-9.3.6.0' + - name: Build gem run: gem build *.gemspec + - name: Publish gem run: | mkdir -p $HOME/.gem diff --git a/.github_changelog_generator b/.github_changelog_generator new file mode 100644 index 0000000..db01582 --- /dev/null +++ b/.github_changelog_generator @@ -0,0 +1,3 @@ +project=vmpooler-provider-vsphere +user=puppetlabs +exclude_labels=maintenance \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cf63c29 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +## [Unreleased](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/HEAD) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/1.6.0...HEAD) + +**Merged pull requests:** + +- \(RE-15111\) Migrate Snyk to Mend Scanning [\#22](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/22) ([yachub](https://github.com/yachub)) +- \(RE-14811\) Remove DIO as codeowners [\#21](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/21) ([yachub](https://github.com/yachub)) +- Add Snyk action [\#20](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/20) ([yachub](https://github.com/yachub)) +- Add release-engineering to codeowners [\#19](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/19) ([yachub](https://github.com/yachub)) +- Update rubocop requirement from ~\> 1.1.0 to ~\> 1.28.2 [\#17](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/17) ([dependabot[bot]](https://github.com/apps/dependabot)) + +## [1.6.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/1.6.0) (2022-07-25) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/1.5.0...1.6.0) + +**Merged pull requests:** + +- pin to vmpooler 2.4 [\#18](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/18) ([sbeaulie](https://github.com/sbeaulie)) + +## [1.5.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/1.5.0) (2021-12-13) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/1.4.0...1.5.0) + +**Merged pull requests:** + +- Bump version to 1.5.0, require vmpooler \>= 2.1 [\#5](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/5) ([genebean](https://github.com/genebean)) +- Move vsphere specific methods out of vmpooler [\#4](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/4) ([sbeaulie](https://github.com/sbeaulie)) + +## [1.4.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/1.4.0) (2021-12-08) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/1.3.0...1.4.0) + +**Merged pull requests:** + +- Prep for initial standalone release: v1.4.0 [\#3](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/3) ([genebean](https://github.com/genebean)) +- Add GH Action for releasing gems [\#2](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/2) ([genebean](https://github.com/genebean)) +- Fix naming, add docs, add missing test file [\#1](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/1) ([genebean](https://github.com/genebean)) + +## [1.3.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/1.3.0) (2021-11-29) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/a08cba099f867b1db01a50940ec3ae9239245db5...1.3.0) + + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/README.md b/README.md index be6485b..f5af6ce 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ - [vmpooler-provider-vsphere](#vmpooler-provider-vsphere) - [Usage](#usage) - [Custom VM Config Attribute](#custom-vm-config-attribute) + - [Releasing](#releasing) - [License](#license) This is a provider for [VMPooler](https://github.com/puppetlabs/vmpooler) allows using vSphere as a source of machines. This provider was originally part of the main VMPooler code base but was extracted to be a standalone gem so that development could be done independently of VMPooler itself. @@ -22,6 +23,15 @@ Linux or Windows Guest: `vmtoolsd --cmd "info-get guestinfo.hostname"` See the [VMware Tools Administration docs](https://docs.vmware.com/en/VMware-Tools/12.1.0/com.vmware.vsphere.vmwaretools.doc/GUID-D026777B-606D-4442-957A-B953C2049659.html) for more information about querying information from the GuestInfo variable. +## Releasing + +Follow these steps to publish a new GitHub release, and build and push the gem to . + +1. Run `./update-changelog` to update `CHANGELOG.md`. +2. Bump the "VERSION" in `lib/vmpooler-provider-vsphere/version.rb` appropriately based on changes in `CHANGELOG.md` since the last release. +3. Commit and push changes to a new branch, then open a pull request against `main` and be sure to add the "maintenance" label. +4. After the pull request is approved and merged, then navigate to Actions --> Release Gem --> run workflow --> Branch: main --> Run workflow. + ## License vmpooler-provider-vsphere is distributed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). See the [LICENSE](LICENSE) file for more details. diff --git a/update-changelog b/update-changelog new file mode 100755 index 0000000..a7bc056 --- /dev/null +++ b/update-changelog @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +docker run -it --rm -e CHANGELOG_GITHUB_TOKEN -v $(pwd):/usr/local/src/your-app \ + githubchangeloggenerator/github-changelog-generator:1.16.2 \ + github_changelog_generator --future-release $(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev) From 3c973eee1e1455d76dffd1277d8bb2ca0cfbab90 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 25 Jan 2023 11:20:23 -0500 Subject: [PATCH 20/70] vmpooler should be a dependency --- vmpooler-provider-vsphere.gemspec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vmpooler-provider-vsphere.gemspec b/vmpooler-provider-vsphere.gemspec index f40d43e..02561a9 100644 --- a/vmpooler-provider-vsphere.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -16,8 +16,7 @@ Gem::Specification.new do |s| s.files = Dir[ "lib/**/*" ] s.require_paths = ["lib"] s.add_dependency 'rbvmomi', '>= 2.1', '< 4.0' - - s.add_development_dependency 'vmpooler', '~> 2.4' # renaming done in version 2.1 + s.add_dependency 'vmpooler', '~> 2.4' # Testing dependencies s.add_development_dependency 'climate_control', '>= 0.2.0' From 8a58862663afe9287982250eb9f5104f48b26589 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 25 Jan 2023 11:24:40 -0500 Subject: [PATCH 21/70] Migrate to rbvmomi2, a community fork of the original project --- vmpooler-provider-vsphere.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vmpooler-provider-vsphere.gemspec b/vmpooler-provider-vsphere.gemspec index 02561a9..1575ae2 100644 --- a/vmpooler-provider-vsphere.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.files = Dir[ "lib/**/*" ] s.require_paths = ["lib"] - s.add_dependency 'rbvmomi', '>= 2.1', '< 4.0' + s.add_dependency 'rbvmomi2', '>= 3.1', '< 4.0' s.add_dependency 'vmpooler', '~> 2.4' # Testing dependencies From 89b1ce67b86f88d1db675a6b57c9f165b0b76e30 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 27 Jan 2023 16:46:33 -0500 Subject: [PATCH 22/70] Update gemfile.lock --- Gemfile.lock | 91 +++++++++++++++++++++++---------------------- README.md | 7 ++++ update-gemfile-lock | 7 ++++ 3 files changed, 60 insertions(+), 45 deletions(-) create mode 100755 update-gemfile-lock diff --git a/Gemfile.lock b/Gemfile.lock index 4976307..5b7bfc3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,39 +2,40 @@ PATH remote: . specs: vmpooler-provider-vsphere (1.6.0) - rbvmomi (>= 2.1, < 4.0) + rbvmomi2 (>= 3.1, < 4.0) + vmpooler (~> 2.4) GEM remote: https://rubygems.org/ specs: ast (2.4.2) - bindata (2.4.10) + bindata (2.4.14) builder (3.2.4) climate_control (1.2.0) coderay (1.1.3) - concurrent-ruby (1.1.10) - connection_pool (2.2.5) + concurrent-ruby (1.2.0) + connection_pool (2.3.0) deep_merge (1.2.2) diff-lcs (1.5.0) docile (1.4.0) - faraday (2.3.0) - faraday-net_http (~> 2.0) + faraday (2.7.4) + faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) - faraday-net_http (2.0.3) + faraday-net_http (3.0.2) ffi (1.15.5-java) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - json (2.6.2-java) + json (2.6.3-java) method_source (1.0.0) - mock_redis (0.32.0) + mock_redis (0.36.0) ruby2_keywords mustermann (2.0.2) ruby2_keywords (~> 0.0.1) net-ldap (0.17.1) nio4r (2.5.8-java) - nokogiri (1.13.8-java) + nokogiri (1.13.10-java) racc (~> 1.4) - opentelemetry-api (1.0.2) + opentelemetry-api (1.1.0) opentelemetry-common (0.19.6) opentelemetry-api (~> 1.0) opentelemetry-exporter-jaeger (0.20.1) @@ -59,59 +60,59 @@ GEM opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.19.0) - opentelemetry-registry (0.1.0) - opentelemetry-api (~> 1.0.1) + opentelemetry-registry (0.2.0) + opentelemetry-api (~> 1.1) opentelemetry-resource_detectors (0.19.1) google-cloud-env opentelemetry-sdk - opentelemetry-sdk (1.1.0) - opentelemetry-api (~> 1.0) + opentelemetry-sdk (1.2.0) + opentelemetry-api (~> 1.1) opentelemetry-common (~> 0.19.3) - opentelemetry-registry (~> 0.1) + opentelemetry-registry (~> 0.2) opentelemetry-semantic_conventions opentelemetry-semantic_conventions (1.8.0) opentelemetry-api (~> 1.0) optimist (3.0.1) parallel (1.22.1) - parser (3.1.2.0) + parser (3.2.0.0) ast (~> 2.4.1) pickup (0.0.11) prometheus-client (2.1.0) - pry (0.14.1-java) + pry (0.14.2-java) coderay (~> 1.1) method_source (~> 1.0) spoon (~> 0.0) - puma (5.6.4-java) + puma (5.6.5-java) nio4r (~> 2.0) - racc (1.6.0-java) - rack (2.2.4) - rack-protection (2.2.2) + racc (1.6.2-java) + rack (2.2.6.2) + rack-protection (2.2.4) rack rack-test (2.0.2) rack (>= 1.3) rainbow (3.1.1) rake (13.0.6) - rbvmomi (3.0.0) + rbvmomi2 (3.6.0) builder (~> 3.2) json (~> 2.3) - nokogiri (~> 1.10) + nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) - redis (4.7.1) - regexp_parser (2.5.0) + redis (4.8.0) + regexp_parser (2.6.2) rexml (3.2.5) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.0) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.1) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.0) rubocop (1.28.2) parallel (~> 1.10) parser (>= 3.1.0.0) @@ -121,30 +122,30 @@ GEM rubocop-ast (>= 1.17.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.19.1) + rubocop-ast (1.24.1) parser (>= 3.1.1.0) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) - simplecov (0.21.2) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) - sinatra (2.2.2) + sinatra (2.2.4) mustermann (~> 2.0) rack (~> 2.2) - rack-protection (= 2.2.2) + rack-protection (= 2.2.4) tilt (~> 2.0) - spicy-proton (2.1.14) + spicy-proton (2.1.15) bindata (~> 2.3) spoon (0.0.6) ffi statsd-ruby (1.5.0) thor (1.2.1) - thrift (0.16.0) + thrift (0.17.0) tilt (2.0.11) - unicode-display_width (2.1.0) + unicode-display_width (2.4.2) vmpooler (2.4.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) @@ -171,6 +172,7 @@ GEM rspec (~> 3) PLATFORMS + universal-java-1.8 universal-java-11 DEPENDENCIES @@ -182,7 +184,6 @@ DEPENDENCIES rubocop (~> 1.28.2) simplecov (>= 0.11.2) thor (~> 1.0, >= 1.0.1) - vmpooler (~> 2.4) vmpooler-provider-vsphere! yarjuf (>= 2.0) diff --git a/README.md b/README.md index f5af6ce..47e5b88 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ - [vmpooler-provider-vsphere](#vmpooler-provider-vsphere) - [Usage](#usage) - [Custom VM Config Attribute](#custom-vm-config-attribute) + - [Update the Gemfile Lock](#update-the-gemfile-lock) - [Releasing](#releasing) - [License](#license) @@ -23,6 +24,12 @@ Linux or Windows Guest: `vmtoolsd --cmd "info-get guestinfo.hostname"` See the [VMware Tools Administration docs](https://docs.vmware.com/en/VMware-Tools/12.1.0/com.vmware.vsphere.vmwaretools.doc/GUID-D026777B-606D-4442-957A-B953C2049659.html) for more information about querying information from the GuestInfo variable. +## Update the Gemfile Lock + +To update the `Gemfile.lock` run `./update-gemfile-lock`. + +Verify, and update if needed, that the docker tag in the script and GitHub action workflows matches what is used in the [vmpooler-deployment Dockerfile](https://github.com/puppetlabs/vmpooler-deployment/blob/main/docker/Dockerfile). + ## Releasing Follow these steps to publish a new GitHub release, and build and push the gem to . diff --git a/update-gemfile-lock b/update-gemfile-lock new file mode 100755 index 0000000..74e9c78 --- /dev/null +++ b/update-gemfile-lock @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment +docker run -it --rm \ + -v $(pwd):/app \ + jruby:9.3.6-jdk \ + /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends make git && cd /app && gem install bundler && bundle install --jobs 3 && bundle update; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' From 68673939f8a9d050f762854002ca993aab6b8fdf Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 27 Jan 2023 15:51:58 -0500 Subject: [PATCH 23/70] Add next version of jruby to testing --- .github/workflows/testing.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 068495e..9e9e9ff 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -19,6 +19,7 @@ jobs: matrix: ruby-version: - 'jruby-9.3.6.0' + - 'jruby-9.4.0.0' steps: - uses: actions/checkout@v2 - name: Set up Ruby @@ -35,6 +36,7 @@ jobs: matrix: ruby-version: - 'jruby-9.3.6.0' + - 'jruby-9.4.0.0' steps: - uses: actions/checkout@v2 - name: Set up Ruby From 960ebc8cc2faee1fe0ebdf7e21e712d6bef7b539 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 27 Jan 2023 16:58:26 -0500 Subject: [PATCH 24/70] Fix more deprecated actions --- .github/workflows/testing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 9e9e9ff..86955f6 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -21,7 +21,7 @@ jobs: - 'jruby-9.3.6.0' - 'jruby-9.4.0.0' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: @@ -38,7 +38,7 @@ jobs: - 'jruby-9.3.6.0' - 'jruby-9.4.0.0' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: From c31dda9b1a39657a356e55c77745f560bf0c773c Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 30 Jan 2023 09:31:43 -0500 Subject: [PATCH 25/70] 2.0.0 release prep --- CHANGELOG.md | 13 +++++++++++-- Gemfile.lock | 2 +- README.md | 9 +++++---- lib/vmpooler-provider-vsphere/version.rb | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf63c29..89ae8c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,20 @@ # Changelog -## [Unreleased](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/HEAD) +## [2.0.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/2.0.0) (2023-01-30) -[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/1.6.0...HEAD) +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/1.6.0...2.0.0) + +**Implemented enhancements:** + +- Migrate to rbvmomi2 [\#25](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/25) ([yachub](https://github.com/yachub)) + +**Closed issues:** + +- Document Custom VM Attribute [\#23](https://github.com/puppetlabs/vmpooler-provider-vsphere/issues/23) **Merged pull requests:** +- Fix workflow deprecations, changelog, and add docs [\#24](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/24) ([yachub](https://github.com/yachub)) - \(RE-15111\) Migrate Snyk to Mend Scanning [\#22](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/22) ([yachub](https://github.com/yachub)) - \(RE-14811\) Remove DIO as codeowners [\#21](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/21) ([yachub](https://github.com/yachub)) - Add Snyk action [\#20](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/20) ([yachub](https://github.com/yachub)) diff --git a/Gemfile.lock b/Gemfile.lock index 5b7bfc3..d12cb1d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (1.6.0) + vmpooler-provider-vsphere (2.0.0) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 2.4) diff --git a/README.md b/README.md index 47e5b88..1c29da7 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,11 @@ Verify, and update if needed, that the docker tag in the script and GitHub actio Follow these steps to publish a new GitHub release, and build and push the gem to . -1. Run `./update-changelog` to update `CHANGELOG.md`. -2. Bump the "VERSION" in `lib/vmpooler-provider-vsphere/version.rb` appropriately based on changes in `CHANGELOG.md` since the last release. -3. Commit and push changes to a new branch, then open a pull request against `main` and be sure to add the "maintenance" label. -4. After the pull request is approved and merged, then navigate to Actions --> Release Gem --> run workflow --> Branch: main --> Run workflow. +1. Bump the "VERSION" in `lib/vmpooler-provider-vsphere/version.rb` appropriately based on changes in `CHANGELOG.md` since the last release. +2. Run `./update-gemfile-lock` to update `Gemfile.lock`. +3. Run `./update-changelog` to update `CHANGELOG.md`. +4. Commit and push changes to a new branch, then open a pull request against `main` and be sure to add the "maintenance" label. +5. After the pull request is approved and merged, then navigate to Actions --> Release Gem --> run workflow --> Branch: main --> Run workflow. ## License diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index e8ab665..96f1982 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '1.6.0' + VERSION = '2.0.0' end From 444ef7a729f3e1de851d45762798664688c55133 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 30 Jan 2023 09:49:45 -0500 Subject: [PATCH 26/70] Fix bad copy/paste in release action --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a1cb5b..2c55e95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 with: args: >- - --future-release ${{ steps.nv.outputs.version }} + --future-release ${{ steps.gv.outputs.version }} env: CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -33,7 +33,7 @@ jobs: echo echo "The following changes were detected:" git --no-pager diff - echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running 'docker run -it --rm -e CHANGELOG_GITHUB_TOKEN -v "\$\(pwd\)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator:1.16.2 github_changelog_generator --future-release ${{ steps.nv.outputs.version }}'" + echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running `./update-changelog`" exit 1 fi @@ -42,7 +42,7 @@ jobs: with: args: >- --since-tag ${{ steps.cv.outputs.result }} - --future-release ${{ steps.nv.outputs.version }} + --future-release ${{ steps.gv.outputs.version }} --output release-notes.md env: CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -50,7 +50,7 @@ jobs: - name: Tag Release uses: ncipollo/release-action@v1 with: - tag: ${{ steps.nv.outputs.version }} + tag: ${{ steps.gv.outputs.version }} token: ${{ secrets.GITHUB_TOKEN }} bodyfile: release-notes.md draft: false From e5aa4f9dfa0391c8af9bb77990b146e5c23f1406 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 30 Jan 2023 09:57:57 -0500 Subject: [PATCH 27/70] Fix another bad copy/paste in release action --- .github/workflows/release.yml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c55e95..2251c3f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,21 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Get Version - id: gv + - name: Get Current Version + uses: actions/github-script@v6 + id: cv + with: + script: | + const { data: response } = await github.rest.repos.getLatestRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + }) + console.log(`The latest release is ${response.tag_name}`) + return response.tag_name + result-encoding: string + + - name: Get Next Version + id: nv run: | version=$(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev) echo "version=$version" >> $GITHUB_OUTPUT @@ -20,7 +33,7 @@ jobs: uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 with: args: >- - --future-release ${{ steps.gv.outputs.version }} + --future-release ${{ steps.nv.outputs.version }} env: CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -42,7 +55,7 @@ jobs: with: args: >- --since-tag ${{ steps.cv.outputs.result }} - --future-release ${{ steps.gv.outputs.version }} + --future-release ${{ steps.nv.outputs.version }} --output release-notes.md env: CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -50,7 +63,7 @@ jobs: - name: Tag Release uses: ncipollo/release-action@v1 with: - tag: ${{ steps.gv.outputs.version }} + tag: ${{ steps.nv.outputs.version }} token: ${{ secrets.GITHUB_TOKEN }} bodyfile: release-notes.md draft: false From 713a4a2c9d5f304b43a626020d139d4596018726 Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Thu, 2 Mar 2023 11:27:55 -0800 Subject: [PATCH 28/70] (maint) Use timeout builtin to TCPSocket when opening sockets. --- lib/vmpooler/providers/vsphere.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index c572d48..e76e972 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -632,15 +632,13 @@ module Vmpooler # This should supercede the open_socket method in the Pool Manager def open_socket(host, domain = nil, timeout = 5, port = 22, &_block) - Timeout.timeout(timeout) do - target_host = host - target_host = "#{host}.#{domain}" if domain - sock = TCPSocket.new target_host, port - begin - yield sock if block_given? - ensure - sock.close - end + target_host = host + target_host = "#{host}.#{domain}" if domain + sock = TCPSocket.new(target_host, port, connect_timeout: timeout) + begin + yield sock if block_given? + ensure + sock.close end end From 5e1891b990dd72ae6210ffae7fa3148243ea7847 Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Thu, 2 Mar 2023 11:38:53 -0800 Subject: [PATCH 29/70] (maint) Update to jruby-9.4.1.0. --- .github/workflows/release.yml | 2 +- .github/workflows/testing.yml | 6 ++---- Gemfile.lock | 24 ++++++++++++------------ update-gemfile-lock | 4 ++-- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2251c3f..0761b75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,7 +73,7 @@ jobs: - name: Install Ruby jruby-9.3.6.0 uses: ruby/setup-ruby@v1 with: - ruby-version: 'jruby-9.3.6.0' + ruby-version: 'jruby-9.4.1.0' - name: Build gem run: gem build *.gemspec diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 86955f6..eac55b3 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,8 +18,7 @@ jobs: strategy: matrix: ruby-version: - - 'jruby-9.3.6.0' - - 'jruby-9.4.0.0' + - 'jruby-9.4.1.0' steps: - uses: actions/checkout@v3 - name: Set up Ruby @@ -35,8 +34,7 @@ jobs: strategy: matrix: ruby-version: - - 'jruby-9.3.6.0' - - 'jruby-9.4.0.0' + - 'jruby-9.4.1.0' steps: - uses: actions/checkout@v3 - name: Set up Ruby diff --git a/Gemfile.lock b/Gemfile.lock index d12cb1d..1821170 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,11 +9,11 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - bindata (2.4.14) + bindata (2.4.15) builder (3.2.4) climate_control (1.2.0) coderay (1.1.3) - concurrent-ruby (1.2.0) + concurrent-ruby (1.2.2) connection_pool (2.3.0) deep_merge (1.2.2) diff-lcs (1.5.0) @@ -33,7 +33,7 @@ GEM ruby2_keywords (~> 0.0.1) net-ldap (0.17.1) nio4r (2.5.8-java) - nokogiri (1.13.10-java) + nokogiri (1.14.2-java) racc (~> 1.4) opentelemetry-api (1.1.0) opentelemetry-common (0.19.6) @@ -74,7 +74,7 @@ GEM opentelemetry-api (~> 1.0) optimist (3.0.1) parallel (1.22.1) - parser (3.2.0.0) + parser (3.2.1.0) ast (~> 2.4.1) pickup (0.0.11) prometheus-client (2.1.0) @@ -97,14 +97,14 @@ GEM json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) - redis (4.8.0) - regexp_parser (2.6.2) + redis (4.8.1) + regexp_parser (2.7.0) rexml (3.2.5) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) rspec-mocks (~> 3.12.0) - rspec-core (3.12.0) + rspec-core (3.12.1) rspec-support (~> 3.12.0) rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) @@ -122,9 +122,9 @@ GEM rubocop-ast (>= 1.17.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.24.1) - parser (>= 3.1.1.0) - ruby-progressbar (1.11.0) + rubocop-ast (1.27.0) + parser (>= 3.2.1.0) + ruby-progressbar (1.12.0) ruby2_keywords (0.0.5) simplecov (0.22.0) docile (~> 1.1) @@ -143,8 +143,8 @@ GEM ffi statsd-ruby (1.5.0) thor (1.2.1) - thrift (0.17.0) - tilt (2.0.11) + thrift (0.18.1) + tilt (2.1.0) unicode-display_width (2.4.2) vmpooler (2.4.0) concurrent-ruby (~> 1.1) diff --git a/update-gemfile-lock b/update-gemfile-lock index 74e9c78..c1f2491 100755 --- a/update-gemfile-lock +++ b/update-gemfile-lock @@ -3,5 +3,5 @@ # The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment docker run -it --rm \ -v $(pwd):/app \ - jruby:9.3.6-jdk \ - /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends make git && cd /app && gem install bundler && bundle install --jobs 3 && bundle update; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' + jruby:9.4.1.0-jdk8 \ + /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends git make netbase && cd /app && gem install bundler && bundle install --jobs 3 && bundle update; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' From 0fb77b917d8caba64bbf46680cfa907cffd99529 Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Thu, 2 Mar 2023 11:50:06 -0800 Subject: [PATCH 30/70] (maint) Update spec tests for timeout change. --- spec/unit/providers/vsphere_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/unit/providers/vsphere_spec.rb b/spec/unit/providers/vsphere_spec.rb index f2a1975..14d9097 100644 --- a/spec/unit/providers/vsphere_spec.rb +++ b/spec/unit/providers/vsphere_spec.rb @@ -1399,44 +1399,44 @@ EOT end it 'opens socket with defaults' do - expect(TCPSocket).to receive(:new).with(hostname,default_socket).and_return(socket) + expect(TCPSocket).to receive(:new).with(hostname,default_socket,{connect_timeout: 5}).and_return(socket) expect(subject.open_socket(hostname)).to eq(nil) end it 'yields the socket if a block is given' do - expect(TCPSocket).to receive(:new).with(hostname,default_socket).and_return(socket) + expect(TCPSocket).to receive(:new).with(hostname,default_socket,{connect_timeout: nil}).and_return(socket) expect{ |socket| subject.open_socket(hostname,nil,nil,default_socket,&socket) }.to yield_control.exactly(1).times end it 'closes the opened socket' do - expect(TCPSocket).to receive(:new).with(hostname,default_socket).and_return(socket) + expect(TCPSocket).to receive(:new).with(hostname,default_socket,{connect_timeout: 5}).and_return(socket) expect(socket).to receive(:close) expect(subject.open_socket(hostname)).to eq(nil) end it 'opens a specific socket' do - expect(TCPSocket).to receive(:new).with(hostname,80).and_return(socket) + expect(TCPSocket).to receive(:new).with(hostname,80,{connect_timeout: nil}).and_return(socket) expect(subject.open_socket(hostname,nil,nil,80)).to eq(nil) end it 'uses a specific domain with the hostname' do - expect(TCPSocket).to receive(:new).with("#{hostname}.#{domain}",default_socket).and_return(socket) + expect(TCPSocket).to receive(:new).with("#{hostname}.#{domain}",default_socket,{connect_timeout: 5}).and_return(socket) expect(subject.open_socket(hostname,domain)).to eq(nil) end it 'raises error if host is not resolvable' do - expect(TCPSocket).to receive(:new).with(hostname,default_socket).and_raise(SocketError,'getaddrinfo: No such host is known') + expect(TCPSocket).to receive(:new).with(hostname,default_socket,{connect_timeout: 1}).and_raise(SocketError,'getaddrinfo: No such host is known') expect { subject.open_socket(hostname,nil,1) }.to raise_error(SocketError) end it 'raises error if socket is not listening' do - expect(TCPSocket).to receive(:new).with(hostname,default_socket).and_raise(SocketError,'No connection could be made because the target machine actively refused it') + expect(TCPSocket).to receive(:new).with(hostname,default_socket,{connect_timeout: 1}).and_raise(SocketError,'No connection could be made because the target machine actively refused it') expect { subject.open_socket(hostname,nil,1) }.to raise_error(SocketError) end From abeabe5a429af114ac1addc969202fc39f533677 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 3 Mar 2023 22:01:40 -0500 Subject: [PATCH 31/70] Bump to java 11 --- update-gemfile-lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update-gemfile-lock b/update-gemfile-lock index c1f2491..46c4873 100755 --- a/update-gemfile-lock +++ b/update-gemfile-lock @@ -3,5 +3,5 @@ # The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment docker run -it --rm \ -v $(pwd):/app \ - jruby:9.4.1.0-jdk8 \ + jruby:9.4.1.0-jdk11 \ /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends git make netbase && cd /app && gem install bundler && bundle install --jobs 3 && bundle update; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' From 32faed5f17a4682e541a7e42663cfcc30f369594 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 6 Mar 2023 14:51:39 -0500 Subject: [PATCH 32/70] Update and consolidate release prep step --- README.md | 7 +++---- release-prep | 15 +++++++++++++++ update-changelog | 5 ----- 3 files changed, 18 insertions(+), 9 deletions(-) create mode 100755 release-prep delete mode 100755 update-changelog diff --git a/README.md b/README.md index 1c29da7..b269094 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,9 @@ Verify, and update if needed, that the docker tag in the script and GitHub actio Follow these steps to publish a new GitHub release, and build and push the gem to . 1. Bump the "VERSION" in `lib/vmpooler-provider-vsphere/version.rb` appropriately based on changes in `CHANGELOG.md` since the last release. -2. Run `./update-gemfile-lock` to update `Gemfile.lock`. -3. Run `./update-changelog` to update `CHANGELOG.md`. -4. Commit and push changes to a new branch, then open a pull request against `main` and be sure to add the "maintenance" label. -5. After the pull request is approved and merged, then navigate to Actions --> Release Gem --> run workflow --> Branch: main --> Run workflow. +2. Run `./release-prep` to update `Gemfile.lock` and `CHANGELOG.md`. +3. Commit and push changes to a new branch, then open a pull request against `main` and be sure to add the "maintenance" label. +4. After the pull request is approved and merged, then navigate to Actions --> Release Gem --> run workflow --> Branch: main --> Run workflow. ## License diff --git a/release-prep b/release-prep new file mode 100755 index 0000000..e3c2b0d --- /dev/null +++ b/release-prep @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment +# +# Update Gemfile.lock +docker run -it --rm \ + -v $(pwd):/app \ + jruby:9.4.1.0-jdk11 \ + /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends git make netbase && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' + +# Update Changelog +docker run -it --rm -e CHANGELOG_GITHUB_TOKEN -v $(pwd):/usr/local/src/your-app \ + githubchangeloggenerator/github-changelog-generator:1.16.2 \ + github_changelog_generator --future-release $(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev) + diff --git a/update-changelog b/update-changelog deleted file mode 100755 index a7bc056..0000000 --- a/update-changelog +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -docker run -it --rm -e CHANGELOG_GITHUB_TOKEN -v $(pwd):/usr/local/src/your-app \ - githubchangeloggenerator/github-changelog-generator:1.16.2 \ - github_changelog_generator --future-release $(grep VERSION lib/vmpooler-provider-vsphere/version.rb |rev |cut -d "'" -f2 |rev) From aba2bb3f0839ee4007dbf9d0b4edd34b3ae011bf Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 6 Mar 2023 14:53:16 -0500 Subject: [PATCH 33/70] 2.1.0 release prep --- CHANGELOG.md | 8 ++++++++ Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89ae8c3..4ffb9ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [2.1.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/2.1.0) (2023-03-06) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/2.0.0...2.1.0) + +**Implemented enhancements:** + +- \(RE-15161\) Use timeout builtin to TCPSocket when opening sockets. [\#30](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/30) ([isaac-hammes](https://github.com/isaac-hammes)) + ## [2.0.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/2.0.0) (2023-01-30) [Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/1.6.0...2.0.0) diff --git a/Gemfile.lock b/Gemfile.lock index 1821170..481fbcb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (2.0.0) + vmpooler-provider-vsphere (2.1.0) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 2.4) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index 96f1982..6be7f16 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '2.0.0' + VERSION = '2.1.0' end From daa49dd1c986556e4f09db64a265d9a65a0e4076 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 15 Feb 2023 09:52:04 -0500 Subject: [PATCH 34/70] Collect the ip address of created VM for use with dns plugins --- lib/vmpooler/providers/vsphere.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index e76e972..6a25053 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -382,11 +382,34 @@ module Vmpooler spec: clone_spec ).wait_for_completion + # Make optional? + ip = get_vm_ip_address(new_vm_object) + + @redis.with_metrics do |redis| + redis.hset("vmpooler__vm__#{new_vmname}", 'ip', ip) + end + vm_hash = generate_vm_hash(new_vm_object, pool_name) end vm_hash end + # The inner method requires vmware tools running in the guest os + def get_vm_ip_address(vm_object, maxloop = 0, loop_delay = 1, max_age = 60) + loop_count = 1 + ip = nil + while ip.nil? + sleep(loop_delay) + ip = vm_object.guest_ip + unless maxloop == 0 + break if loop_count >= maxloop + + loop_count += 1 + end + end + return ip + end + def create_config_spec(vm_name, template_name, extra_config) RbVmomi::VIM.VirtualMachineConfigSpec( annotation: JSON.pretty_generate( From 130736fa9d64c80124873d37330180ad674fd05b Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 15 Feb 2023 15:44:46 -0500 Subject: [PATCH 35/70] Use domain provided by pool's dns_config --- lib/vmpooler/providers/vsphere.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index 6a25053..846ffe1 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -51,6 +51,11 @@ module Vmpooler 'vsphere' end + def domain(pool_name) + dns_plugin_name = pool_config(pool_name)['dns_plugin'] + return dns_config(dns_plugin_name) + end + def folder_configured?(folder_title, base_folder, configured_folders, allowlist) return true if allowlist&.include?(folder_title) return false unless configured_folders.keys.include?(folder_title) @@ -565,7 +570,8 @@ module Vmpooler def vm_ready?(_pool_name, vm_name) begin - open_socket(vm_name, global_config[:config]['domain']) + domain = domain(_pool_name) + open_socket(vm_name, domain) rescue StandardError => _e return false end From c0e59a034733d4108096b6870882ac063d32cc13 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 17 Feb 2023 09:01:42 -0500 Subject: [PATCH 36/70] Refactor obtaining and saving ip address --- lib/vmpooler/providers/vsphere.rb | 43 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index 846ffe1..4e0a1c7 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -387,32 +387,20 @@ module Vmpooler spec: clone_spec ).wait_for_completion - # Make optional? - ip = get_vm_ip_address(new_vm_object) - - @redis.with_metrics do |redis| - redis.hset("vmpooler__vm__#{new_vmname}", 'ip', ip) - end - vm_hash = generate_vm_hash(new_vm_object, pool_name) end vm_hash end # The inner method requires vmware tools running in the guest os - def get_vm_ip_address(vm_object, maxloop = 0, loop_delay = 1, max_age = 60) - loop_count = 1 - ip = nil - while ip.nil? - sleep(loop_delay) - ip = vm_object.guest_ip - unless maxloop == 0 - break if loop_count >= maxloop - - loop_count += 1 - end + def get_vm_ip_address(vm_name, pool_name) + + @connection_pool.with_metrics do |pool_object| + connection = ensured_vsphere_connection(pool_object) + vm_object = find_vm(pool_name, vm_name, connection) + vm_hash = generate_vm_hash(vm_object, pool_name) + return vm_hash['ip'] end - return ip end def create_config_spec(vm_name, template_name, extra_config) @@ -611,13 +599,28 @@ module Vmpooler boottime = vm_object.runtime.bootTime if vm_object.runtime&.bootTime powerstate = vm_object.runtime.powerState if vm_object.runtime&.powerState + ip_maxloop = 60 + ip_loop_delay = 1 + ip_loop_count = 1 + ip = nil + while ip.nil? + sleep(ip_loop_delay) + ip = vm_object.guest_ip + unless ip_maxloop == 0 + break if ip_loop_count >= ip_maxloop + + ip_loop_count += 1 + end + end + { 'name' => vm_object.name, 'hostname' => hostname, 'template' => pool_configuration['template'], 'poolname' => pool_name, 'boottime' => boottime, - 'powerstate' => powerstate + 'powerstate' => powerstate, + 'ip' => ip } end From 0579f15a4bfee46e7b6bfff449d5f845b118a4f6 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 22 Feb 2023 09:01:39 -0500 Subject: [PATCH 37/70] Fix rubocops --- lib/vmpooler/providers/vsphere.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index 4e0a1c7..99271bc 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -53,7 +53,7 @@ module Vmpooler def domain(pool_name) dns_plugin_name = pool_config(pool_name)['dns_plugin'] - return dns_config(dns_plugin_name) + dns_config(dns_plugin_name) end def folder_configured?(folder_title, base_folder, configured_folders, allowlist) @@ -394,7 +394,6 @@ module Vmpooler # The inner method requires vmware tools running in the guest os def get_vm_ip_address(vm_name, pool_name) - @connection_pool.with_metrics do |pool_object| connection = ensured_vsphere_connection(pool_object) vm_object = find_vm(pool_name, vm_name, connection) @@ -556,9 +555,9 @@ module Vmpooler true end - def vm_ready?(_pool_name, vm_name) + def vm_ready?(pool_name, vm_name) begin - domain = domain(_pool_name) + domain = domain(pool_name) open_socket(vm_name, domain) rescue StandardError => _e return false @@ -620,7 +619,7 @@ module Vmpooler 'poolname' => pool_name, 'boottime' => boottime, 'powerstate' => powerstate, - 'ip' => ip + 'ip' => ip } end From c0a23ff0d02e4ed41df9a574434bf59fff718764 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 22 Feb 2023 10:27:01 -0500 Subject: [PATCH 38/70] Remove redundant pool_manager spec as it's part of vmpooler core --- spec/unit/pool_manager_spec.rb | 4309 -------------------------------- 1 file changed, 4309 deletions(-) delete mode 100644 spec/unit/pool_manager_spec.rb diff --git a/spec/unit/pool_manager_spec.rb b/spec/unit/pool_manager_spec.rb deleted file mode 100644 index 0b14668..0000000 --- a/spec/unit/pool_manager_spec.rb +++ /dev/null @@ -1,4309 +0,0 @@ -require 'spec_helper' -require 'time' -require 'mock_redis' - -# Custom RSpec :Matchers - -# Match a Hashtable['name'] is an expected value -RSpec::Matchers.define :a_pool_with_name_of do |value| - match { |actual| actual['name'] == value } -end -describe 'Pool Manager' do - let(:logger) { MockLogger.new } - let(:metrics) { Vmpooler::Metrics::DummyStatsd.new } - let(:pool) { 'pool1' } - let(:vm) { 'vm1' } - let(:timeout) { 5 } - let(:host) { double('host') } - let(:token) { 'token1234' } - let(:request_id) { '1234' } - let(:current_time) { Time.now } - - let(:provider_options) { {} } - let(:redis_connection_pool) { Vmpooler::PoolManager::GenericConnectionPool.new( - metrics: metrics, - connpool_type: 'redis_connection_pool', - connpool_provider: 'testprovider', - size: 1, - timeout: 5 - ) { MockRedis.new } - } - - let(:provider) { Vmpooler::PoolManager::Provider::Base.new(config, logger, metrics, redis_connection_pool, 'mock_provider', provider_options) } - - let(:config) { YAML.load(<<-EOT ---- -:config: {} -:providers: - :mock: -:redis: {} -:pools: - - name: '#{pool}' - size: 1 -EOT - ) - } - - subject { Vmpooler::PoolManager.new(config, logger, redis_connection_pool, metrics) } - - describe '#config' do - before do - expect(subject).not_to be_nil - end - - it 'should return the current configuration' do - expect(subject.config).to eq(config) - end - end - - describe '#load_used_providers' do - let(:config) { YAML.load(<<-EOT ---- -:config: -:providers: - :vsphere: -:pools: - - name: '#{pool}' - size: 1 - provider: 'spoof' - - name: 'vsphere-pool' - size: 1 - provider: 'vsphere' - EOT - ) - } - it do - expect(subject.load_used_providers.join(', ')).to match(%r{#{project_root_dir}/lib/vmpooler/providers/vsphere.rb}) - expect(subject.load_used_providers.join(', ')).to match(%r{/lib/vmpooler/providers/dummy.rb}) - end - end - - describe '#check_pending_vm' do - before do - expect(subject).not_to be_nil - end - - it 'calls _check_pending_vm' do - expect(Thread).to receive(:new).and_yield - expect(subject).to receive(:_check_pending_vm).with(vm,pool,timeout,provider) - - subject.check_pending_vm(vm, pool, timeout, provider) - end - end - - describe '#_check_pending_vm' do - before do - expect(subject).not_to be_nil - end - - context 'host is in pool' do - - it 'calls move_pending_vm_to_ready if host is ready' do - expect(provider).to receive(:vm_ready?).with(pool,vm).and_return(true) - redis_connection_pool.with do |redis| - expect(subject).to receive(:move_pending_vm_to_ready).with(vm, pool, redis, nil) - end - - subject._check_pending_vm(vm, pool, timeout, provider) - end - - it 'calls fail_pending_vm if host is not ready' do - expect(provider).to receive(:vm_ready?).with(pool,vm).and_return(false) - redis_connection_pool.with do |redis| - expect(subject).to receive(:fail_pending_vm).with(vm, pool, timeout, redis) - end - - subject._check_pending_vm(vm, pool, timeout, provider) - end - end - - context 'with a locked vm mutex' do - let(:mutex) { Mutex.new } - before(:each) do - mutex.lock - end - - it 'should return' do - expect(subject).to receive(:vm_mutex).and_return(mutex) - - expect(subject._check_pending_vm(vm, pool, timeout, provider)).to be_nil - end - end - end - - describe '#remove_nonexistent_vm' do - before do - expect(subject).not_to be_nil - end - - it 'removes VM from pending in redis' do - redis_connection_pool.with do |redis| - create_pending_vm(pool,vm,redis) - - subject.remove_nonexistent_vm(vm, pool, redis) - expect(redis.sismember("vmpooler__pending__#{pool}", vm)).to be(false) - end - end - - it 'logs msg' do - expect(logger).to receive(:log).with('d', "[!] [#{pool}] '#{vm}' no longer exists. Removing from pending.") - - redis_connection_pool.with do |redis| - subject.remove_nonexistent_vm(vm, pool, redis) - end - end - end - - describe '#fail_pending_vm' do - before do - expect(subject).not_to be_nil - end - - before(:each) do - redis_connection_pool.with do |redis| - create_pending_vm(pool,vm,redis) - config[:config]['vm_checktime'] = 15 - end - end - - it 'takes no action if VM is not cloning' do - redis_connection_pool.with do |redis| - expect(subject.fail_pending_vm(vm, pool, timeout, redis)).to eq(true) - end - end - - it 'takes no action if VM is within timeout' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone',Time.now.to_s) - expect(subject.fail_pending_vm(vm, pool, timeout, redis)).to eq(true) - expect(redis.sismember("vmpooler__pending__#{pool}", vm)).to be(true) - end - end - - it 'moves VM to completed queue if VM has exceeded timeout and exists' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone',Date.new(2001,1,1).to_s) - expect(subject.fail_pending_vm(vm, pool, timeout, redis, exists: true)).to eq(true) - expect(redis.sismember("vmpooler__pending__#{pool}", vm)).to be(false) - expect(redis.sismember("vmpooler__completed__#{pool}", vm)).to be(true) - end - end - - it 'logs message if VM has exceeded timeout and exists' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone',Date.new(2001,1,1).to_s) - expect(logger).to receive(:log).with('d', "[!] [#{pool}] '#{vm}' marked as 'failed' after #{timeout} minutes") - expect(subject.fail_pending_vm(vm, pool, timeout, redis, exists: true)).to eq(true) - end - end - - it 'calls remove_nonexistent_vm if VM has exceeded timeout and does not exist' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone',Date.new(2001,1,1).to_s) - expect(subject).to receive(:remove_nonexistent_vm).with(vm, pool, redis) - expect(subject.fail_pending_vm(vm, pool, timeout, redis, exists: false)).to eq(true) - end - end - - it 'swallows error if an error is raised' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone','iamnotparsable_asdate') - expect(subject.fail_pending_vm(vm, pool, timeout, redis, exists: true)).to eq(false) - end - end - - it 'logs message if an error is raised' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone','iamnotparsable_asdate') - expect(logger).to receive(:log).with('d', String) - - subject.fail_pending_vm(vm, pool, timeout, redis, exists: true) - end - end - - context 'with request_id' do - - it 'creates a new odcreate task' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'clone',(Time.now - 900).to_s) - redis.hset("vmpooler__vm__#{vm}", 'pool_alias', pool) - redis.hset("vmpooler__vm__#{vm}", 'request_id', request_id) - subject.fail_pending_vm(vm, pool, timeout, redis, exists: true) - expect(redis.zrange('vmpooler__odcreate__task', 0, -1)).to eq(["#{pool}:#{pool}:1:#{request_id}"]) - end - end - end - end - - describe '#check_ready_vm' do - let(:ttl) { 5 } - let(:poolconfig) { config[:pools][0] } - - before do - expect(subject).not_to be_nil - end - - it 'calls _check_ready_vm' do - expect(Thread).to receive(:new).and_yield - expect(subject).to receive(:_check_ready_vm).with(vm, poolconfig, ttl, provider) - - subject.check_ready_vm(vm, poolconfig, ttl, provider) - end - end - - describe '#_check_ready_vm' do - let(:ttl) { 5 } - let(:host) { {} } - let(:config) { YAML.load(<<-EOT ---- -:config: {} -:providers: - :mock: -:pools: - - name: '#{pool}' - size: 1 -:pool_index: - '#{pool}': 0 -EOT - ) - } - - before(:each) do - redis_connection_pool.with do |redis| - create_ready_vm(pool,vm,redis) - end - config[:config]['vm_checktime'] = 15 - - # Create a VM which is powered on - host['hostname'] = vm - host['powerstate'] = 'PoweredOn' - allow(provider).to receive(:get_vm).with(pool,vm).and_return(host) - end - - context 'a VM that does not need to be checked' do - it 'should do nothing' do - redis_connection_pool.with do |redis| - check_stamp = (Time.now - 60).to_s - redis.hset("vmpooler__vm__#{vm}", 'check', check_stamp) - expect(provider).to receive(:get_vm).exactly(0).times - subject._check_ready_vm(vm, pool, ttl, provider) - expect(redis.hget("vmpooler__vm__#{vm}", 'check')).to eq(check_stamp) - end - end - end - - context 'a VM that has never been checked' do - it 'should set the current check timestamp' do - redis_connection_pool.with do |redis| - expect(redis.hget("vmpooler__vm__#{vm}", 'check')).to be_nil - subject._check_ready_vm(vm, pool, ttl, provider) - expect(redis.hget("vmpooler__vm__#{vm}", 'check')).to_not be_nil - end - end - end - - context 'a VM that needs to be checked' do - let(:last_check_date) { Time.now - 901 } - before(:each) do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'check',last_check_date) - end - end - - it 'should set the current check timestamp' do - redis_connection_pool.with do |redis| - expect(redis.hget("vmpooler__vm__#{vm}", 'check')).to eq(last_check_date.to_s) - subject._check_ready_vm(vm, pool, ttl, provider) - expect(redis.hget("vmpooler__vm__#{vm}", 'check')).to_not eq(last_check_date.to_s) - end - end - - context 'and is ready' do - it 'should only set the next check interval' do - subject._check_ready_vm(vm, pool, ttl, provider) - end - end - - context 'has correct name and is not ready' do - before(:each) do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'ready', Time.now - 200) - redis.sadd("vmpooler__ready__#{pool}", vm) - end - end - - it 'should move the VM to the completed queue' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:smove).with("vmpooler__ready__#{pool}", "vmpooler__completed__#{pool}", vm) - - subject._check_ready_vm(vm, pool, ttl, provider) - end - end - - it 'should move the VM to the completed queue in Redis' do - redis_connection_pool.with do |redis| - subject._check_ready_vm(vm, pool, ttl, provider) - expect(redis.sismember("vmpooler__completed__#{pool}", vm)).to be(true) - end - end - - it 'should log messages about being unreachable' do - expect(logger).to receive(:log).with('d', "[!] [#{pool}] '#{vm}' is unreachable, removed from 'ready' queue") - - subject._check_ready_vm(vm, pool, ttl, provider) - end - end - - context 'with hostname mismatch checking enabled' do - - context 'when less than 60 seconds since a VM moved to ready' do - before(:each) do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'ready', Time.now) - end - end - - it 'should return nil' do - expect(subject._check_ready_vm(vm, pool, ttl, provider)).to be_nil - end - end - - context 'with a hostname mismatch' do - let(:different_hostname) { 'different_name' } - let(:longer_ttl) { 20 } - before(:each) do - host['hostname'] = different_hostname - redis_connection_pool.with do |redis| - expect(subject).to receive(:mismatched_hostname?).with(vm, pool, provider, redis).and_return(true) - redis.hset("vmpooler__vm__#{vm}", 'ready', Time.now - 300) - end - end - - it 'should not run vm_still_ready?' do - expect(subject).to_not receive(:vm_still_ready?) - - subject._check_ready_vm(vm, pool, longer_ttl, provider) - end - end - end - - context 'with hostname mismatch checking disabled on the pool' do - before(:each) do - config[:pools][0]['check_hostname_for_mismatch'] = false - end - - it 'should not run get_vm' do - expect(provider).to_not receive(:get_vm) - - subject._check_ready_vm(vm, pool, ttl, provider) - end - end - - context 'with hostname mismatch checking disabled in config' do - before(:each) do - config[:config]['check_ready_vm_hostname_for_mismatch'] = false - end - - it 'should not run get_vm' do - expect(provider).to_not receive(:get_vm) - - subject._check_ready_vm(vm, pool, ttl, provider) - end - end - end - - context 'with a locked vm mutex' do - let(:mutex) { Mutex.new } - before(:each) do - mutex.lock - end - - it 'should return' do - expect(subject).to receive(:vm_mutex).and_return(mutex) - - expect(subject._check_ready_vm(vm, pool, ttl, provider)).to be_nil - end - end - end - - describe '#check_running_vm' do - let(:provider) { double('provider') } - let (:ttl) { 5 } - - before do - expect(subject).not_to be_nil - end - - it 'calls _check_running_vm' do - expect(Thread).to receive(:new).and_yield - expect(subject).to receive(:_check_running_vm).with(vm, pool, ttl, provider) - - subject.check_running_vm(vm, pool, ttl, provider) - end - end - - describe '#_check_running_vm' do - let(:host) { {} } - - before do - expect(subject).not_to be_nil - end - - before(:each) do - redis_connection_pool.with do |redis| - create_running_vm(pool,vm, redis) - end - - # Create a VM which is powered on - host['hostname'] = vm - host['powerstate'] = 'PoweredOn' - allow(provider).to receive(:get_vm).with(pool,vm).and_return(host) - end - - it 'moves a missing VM to the completed queue' do - redis_connection_pool.with do |redis| - expect(provider).to receive(:vm_ready?).and_return(false) - expect(provider).to receive(:get_vm).with(pool,vm).and_return(nil) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(true) - subject._check_running_vm(vm, pool, timeout, provider) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(false) - end - end - - context 'valid host' do - it 'should kill a VM if it has no checkout time' do - redis_connection_pool.with do |redis| - expect(provider).to receive(:vm_ready?).and_return(true) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(true) - subject._check_running_vm(vm, pool, 0, provider) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(false) - end - end - - it 'should log a message when the machine is removed due to no active data' do - redis_connection_pool.with do |redis| - expect(provider).to receive(:vm_ready?).and_return(true) - expect(logger).to receive(:log).with('d',"[!] [#{pool}] '#{vm}' is listed as running, but has no checkouttime data. Removing from running") - subject._check_running_vm(vm, pool, 0, provider) - end - end - - it 'should not move VM if TTL is zero' do - redis_connection_pool.with do |redis| - expect(provider).to receive(:vm_ready?).and_return(true) - redis.hset("vmpooler__active__#{pool}", vm,(Time.now - timeout*60*60).to_s) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(true) - subject._check_running_vm(vm, pool, 0, provider) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(true) - end - end - - it 'should move VM when past TTL' do - redis_connection_pool.with do |redis| - redis.hset("vmpooler__active__#{pool}", vm,(Time.now - timeout*60*60).to_s) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(true) - expect(redis.sismember("vmpooler__completed__#{pool}", vm)).to be(false) - subject._check_running_vm(vm, pool, timeout, provider) - expect(redis.sismember("vmpooler__running__#{pool}", vm)).to be(false) - expect(redis.sismember("vmpooler__completed__#{pool}", vm)).to be(true) - end - end - end - - context 'with a locked vm mutex' do - let(:mutex) { Mutex.new } - before(:each) do - mutex.lock - end - - it 'should return' do - expect(subject).to receive(:vm_mutex).and_return(mutex) - - expect(subject._check_running_vm(vm, pool, timeout, provider)).to be_nil - end - end - end - - describe '#move_vm_queue' do - let(:queue_from) { 'pending' } - let(:queue_to) { 'completed' } - let(:message) { 'message' } - - before do - expect(subject).not_to be_nil - end - - before(:each) do - redis_connection_pool.with do |redis| - create_pending_vm(pool, vm, redis, token) - end - end - - it 'VM should be in the "from queue" before the move' do - redis_connection_pool.with do |redis| - expect(redis.sismember("vmpooler__#{queue_from}__#{pool}",vm)) - end - end - - it 'VM should not be in the "from queue" after the move' do - redis_connection_pool.with do |redis| - subject.move_vm_queue(pool, vm, queue_from, queue_to, redis, message) - expect(!redis.sismember("vmpooler__#{queue_from}__#{pool}",vm)) - end - end - - it 'VM should not be in the "to queue" before the move' do - redis_connection_pool.with do |redis| - expect(!redis.sismember("vmpooler__#{queue_to}__#{pool}",vm)) - end - end - - it 'VM should be in the "to queue" after the move' do - redis_connection_pool.with do |redis| - subject.move_vm_queue(pool, vm, queue_from, queue_to, redis, message) - expect(redis.sismember("vmpooler__#{queue_to}__#{pool}",vm)) - end - end - - it 'should log a message' do - allow(logger).to receive(:log).with('d', "[!] [#{pool}] '#{vm}' #{message}") - - redis_connection_pool.with do |redis| - subject.move_vm_queue(pool, vm, queue_from, queue_to, redis, message) - end - end - end - - describe '#clone_vm' do - let (:pool_object) { { 'name' => pool } } - - before do - expect(subject).not_to be_nil - expect(Thread).to receive(:new).and_yield - end - - it 'calls _clone_vm' do - expect(subject).to receive(:_clone_vm).with(pool_object,provider,nil,nil) - - subject.clone_vm(pool_object,provider) - end - - it 'logs a message if an error is raised' do - allow(logger).to receive(:log) - expect(logger).to receive(:log).with('s',"[!] [#{pool}] failed while cloning VM with an error: MockError") - expect(subject).to receive(:_clone_vm).with(pool,provider,nil,nil).and_raise('MockError') - - expect{subject.clone_vm(pool,provider)}.to raise_error(/MockError/) - end - end - - describe '#destroy_vm' do - before do - expect(subject).not_to be_nil - expect(Thread).to receive(:new).and_yield - end - - it 'calls _destroy_vm' do - expect(subject).to receive(:_destroy_vm).with(vm,pool,provider) - - subject.destroy_vm(vm,pool,provider) - end - - it 'logs a message if an error is raised' do - allow(logger).to receive(:log) - expect(logger).to receive(:log).with('d',"[!] [#{pool}] '#{vm}' failed while destroying the VM with an error: MockError") - expect(subject).to receive(:_destroy_vm).with(vm,pool,provider).and_raise('MockError') - - expect{subject.destroy_vm(vm,pool,provider)}.to raise_error(/MockError/) - end - end - - describe '#purge_unused_vms_and_resources' do - let(:config) { YAML.load(<<-EOT ---- -:config: {} -:providers: - :mock: {} -:pools: - - name: '#{pool}' - size: 1 -EOT - ) - } - - it 'should return when purging is not enabled' do - expect(subject.purge_unused_vms_and_resources).to be_nil - end - - context 'with purging enabled globally' do - before(:each) do - config[:config]['purge_unconfigured_resources'] = true - expect(Thread).to receive(:new).and_yield - end - - it 'should run a purge for each provider' do - expect(subject).to receive(:purge_vms_and_resources) - - subject.purge_unused_vms_and_resources - end - - it 'should log when purging fails' do - expect(subject).to receive(:purge_vms_and_resources).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('s', '[!] failed while purging provider mock VMs and folders with an error: MockError') - - subject.purge_unused_vms_and_resources - end - end - - context 'with purging enabled on the provider' do - before(:each) do - config[:providers][:mock]['purge_unconfigured_resources'] = true - expect(Thread).to receive(:new).and_yield - end - - it 'should run a purge for the provider' do - expect(subject).to receive(:purge_vms_and_resources) - - subject.purge_unused_vms_and_resources - end - end - end - - describe '#purge_vms_and_resources' do - let(:provider_name) { 'mock_provider' } - let(:allowlist) { nil } - let(:config) { YAML.load(<<-EOT ---- -:config: {} -:providers: - :mock_provider: {} -:pools: - - name: '#{pool}' - size: 1 - provider: '#{provider_name}' -EOT - ) - } - - context 'when purging folders' do - before do - expect(subject).not_to be_nil - # Inject mock provider into global variable - Note this is a code smell - $providers = { provider_name => provider } - end - - it 'should run purge_unconfigured_resources' do - expect(provider).to receive(:purge_unconfigured_resources).with(allowlist) - allow(provider).to receive(:provider_config).and_return({}) - - subject.purge_vms_and_resources(provider_name) - end - - it 'should raise any errors' do - expect(provider).to receive(:purge_unconfigured_resources).with(allowlist).and_raise('mockerror') - allow(provider).to receive(:provider_config).and_return({}) - - expect{ subject.purge_vms_and_resources(provider_name) }.to raise_error(RuntimeError, 'mockerror') - end - end - end - - describe '#create_vm_disk' do - let(:provider) { double('provider') } - let(:disk_size) { 15 } - - before do - expect(subject).not_to be_nil - end - - it 'calls _create_vm_disk' do - expect(Thread).to receive(:new).and_yield - expect(subject).to receive(:_create_vm_disk).with(pool, vm, disk_size, provider) - - subject.create_vm_disk(pool, vm, disk_size, provider) - end - end - - describe "#_create_vm_disk" do - let(:disk_size) { '15' } - - before(:each) do - expect(subject).not_to be_nil - allow(logger).to receive(:log) - - redis_connection_pool.with do |redis| - create_running_vm(pool,vm,redis,token) - end - end - - context 'Given a VM that does not exist' do - before(:each) do - # As per base_spec, create_disk will raise if the VM does not exist - expect(provider).to receive(:create_disk).with(pool,vm,disk_size.to_i).and_raise("VM #{vm} does not exist") - end - - it 'should not update redis if the VM does not exist' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hset).exactly(0).times - expect{ subject._create_vm_disk(pool, vm, disk_size, provider) }.to raise_error(RuntimeError) - end - end - end - - context 'Given an invalid disk size' do - [{ :description => 'is nil', :value => nil }, - { :description => 'is an empty string', :value => '' }, - { :description => 'is less than 1', :value => '0' }, - { :description => 'cannot be converted to an integer', :value => 'abc123' }, - ].each do |testcase| - it "should not attempt the create the disk if the disk size #{testcase[:description]}" do - expect(provider).to receive(:create_disk).exactly(0).times - expect{ subject._create_vm_disk(pool, vm, testcase[:value], provider) }.to raise_error(/Invalid disk size/) - end - end - - it 'should raise an error if the disk size is a Fixnum' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hset).exactly(0).times - expect{ subject._create_vm_disk(pool, vm, 10, provider) }.to raise_error(NoMethodError,/empty?/) - end - end - end - - context 'Given a successful disk creation' do - before(:each) do - expect(provider).to receive(:create_disk).with(pool,vm,disk_size.to_i).and_return(true) - end - - it 'should log a message' do - expect(logger).to receive(:log).with('s', "[ ] [disk_manager] '#{vm}' is attaching a #{disk_size}gb disk") - expect(logger).to receive(:log).with('s', /\[\+\] \[disk_manager\] '#{vm}' attached #{disk_size}gb disk in 0.[\d]+ seconds/) - - subject._create_vm_disk(pool, vm, disk_size, provider) - end - - it 'should update redis information when attaching the first disk' do - redis_connection_pool.with do |redis| - subject._create_vm_disk(pool, vm, disk_size, provider) - expect(redis.hget("vmpooler__vm__#{vm}", 'disk')).to eq("+#{disk_size}gb") - end - end - - it 'should update redis information when attaching the additional disks' do - redis_connection_pool.with do |redis| - initial_disks = '+10gb:+20gb' - redis.hset("vmpooler__vm__#{vm}", 'disk', initial_disks) - - subject._create_vm_disk(pool, vm, disk_size, provider) - expect(redis.hget("vmpooler__vm__#{vm}", 'disk')).to eq("#{initial_disks}:+#{disk_size}gb") - end - end - end - - context 'Given a failed disk creation' do - before(:each) do - expect(provider).to receive(:create_disk).with(pool,vm,disk_size.to_i).and_return(false) - end - - it 'should not update redis information' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hset).exactly(0).times - - subject._create_vm_disk(pool, vm, disk_size, provider) - expect(redis.hget("vmpooler__vm__#{vm}", 'disk')).to be_nil - end - end - - it 'should log a message' do - expect(logger).to receive(:log).with('s', "[+] [disk_manager] '#{vm}' failed to attach disk") - - subject._create_vm_disk(pool, vm, disk_size, provider) - end - end - end - - describe '#create_vm_snapshot' do - let(:snapshot_name) { 'snapshot' } - - before do - expect(subject).not_to be_nil - end - - it 'calls _create_vm_snapshot' do - expect(Thread).to receive(:new).and_yield - expect(subject).to receive(:_create_vm_snapshot).with(pool, vm, snapshot_name, provider) - - subject.create_vm_snapshot(pool, vm, snapshot_name, provider) - end - end - - describe '#_create_vm_snapshot' do - let(:snapshot_name) { 'snapshot1' } - - before do - expect(subject).not_to be_nil - end - - before(:each) do - redis_connection_pool.with do |redis| - create_running_vm(pool,vm,redis,token) - end - end - - context 'Given a Pool that does not exist' do - let(:missing_pool) { 'missing_pool' } - - before(:each) do - expect(provider).to receive(:create_snapshot).with(missing_pool, vm, snapshot_name).and_raise("Pool #{missing_pool} not found") - end - - it 'should not update redis' do - redis_connection_pool.with do |redis| - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to be_nil - expect{ subject._create_vm_snapshot(missing_pool, vm, snapshot_name, provider) }.to raise_error("Pool #{missing_pool} not found") - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to be_nil - end - end - end - - context 'Given a VM that does not exist' do - let(:missing_vm) { 'missing_vm' } - before(:each) do - expect(provider).to receive(:create_snapshot).with(pool, missing_vm, snapshot_name).and_raise("VM #{missing_vm} not found") - end - - it 'should not update redis' do - redis_connection_pool.with do |redis| - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to be_nil - expect{ subject._create_vm_snapshot(pool, missing_vm, snapshot_name, provider) }.to raise_error("VM #{missing_vm} not found") - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to be_nil - end - end - end - - context 'Given a snapshot creation that succeeds' do - before(:each) do - expect(provider).to receive(:create_snapshot).with(pool, vm, snapshot_name).and_return(true) - end - - it 'should log messages' do - expect(logger).to receive(:log).with('s', "[ ] [snapshot_manager] 'Attempting to snapshot #{vm} in pool #{pool}") - expect(logger).to receive(:log).with('s', /\[\+\] \[snapshot_manager\] '#{vm}' snapshot created in 0.[\d]+ seconds/) - - subject._create_vm_snapshot(pool, vm, snapshot_name, provider) - end - - it 'should add snapshot redis information' do - redis_connection_pool.with do |redis| - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to be_nil - subject._create_vm_snapshot(pool, vm, snapshot_name, provider) - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to_not be_nil - end - end - end - - context 'Given a snapshot creation that fails' do - before(:each) do - expect(provider).to receive(:create_snapshot).with(pool, vm, snapshot_name).and_return(false) - end - - it 'should log messages' do - expect(logger).to receive(:log).with('s', "[ ] [snapshot_manager] 'Attempting to snapshot #{vm} in pool #{pool}") - expect(logger).to receive(:log).with('s', "[+] [snapshot_manager] Failed to snapshot '#{vm}'") - - subject._create_vm_snapshot(pool, vm, snapshot_name, provider) - end - - it 'should not update redis' do - redis_connection_pool.with do |redis| - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to be_nil - subject._create_vm_snapshot(pool, vm, snapshot_name, provider) - expect(redis.hget("vmpooler__vm__#{vm}", "snapshot:#{snapshot_name}")).to be_nil - end - end - end - end - - describe '#revert_vm_snapshot' do - let(:snapshot_name) { 'snapshot' } - - before do - expect(subject).not_to be_nil - end - - it 'calls _revert_vm_snapshot' do - expect(Thread).to receive(:new).and_yield - expect(subject).to receive(:_revert_vm_snapshot).with(pool, vm, snapshot_name, provider) - - subject.revert_vm_snapshot(pool, vm, snapshot_name, provider) - end - end - - describe '#_revert_vm_snapshot' do - let(:snapshot_name) { 'snapshot1' } - - before do - expect(subject).not_to be_nil - end - - context 'Given a Pool that does not exist' do - let(:missing_pool) { 'missing_pool' } - - before(:each) do - expect(provider).to receive(:revert_snapshot).with(missing_pool, vm, snapshot_name).and_raise("Pool #{missing_pool} not found") - end - - it 'should not log a result message' do - expect(logger).to receive(:log).with('s', /\[\+\] \[snapshot_manager\] '#{vm}' reverted to snapshot '#{snapshot_name}' in 0.[\d]+ seconds/).exactly(0).times - expect(logger).to receive(:log).with('s', "[+] [snapshot_manager] Failed to revert #{vm}' in pool #{missing_pool} to snapshot '#{snapshot_name}'").exactly(0).times - - expect{ subject._revert_vm_snapshot(missing_pool, vm, snapshot_name, provider) }.to raise_error("Pool #{missing_pool} not found") - end - end - - context 'Given a VM that does not exist' do - let(:missing_vm) { 'missing_vm' } - before(:each) do - expect(provider).to receive(:revert_snapshot).with(pool, missing_vm, snapshot_name).and_raise("VM #{missing_vm} not found") - end - - it 'should not log a result message' do - expect(logger).to receive(:log).with('s', /\[\+\] \[snapshot_manager\] '#{missing_vm}' reverted to snapshot '#{snapshot_name}' in 0.[\d]+ seconds/).exactly(0).times - expect(logger).to receive(:log).with('s', "[+] [snapshot_manager] Failed to revert #{missing_vm}' in pool #{pool} to snapshot '#{snapshot_name}'").exactly(0).times - - expect{ subject._revert_vm_snapshot(pool, missing_vm, snapshot_name, provider) }.to raise_error("VM #{missing_vm} not found") - end - end - - context 'Given a snapshot revert that succeeds' do - before(:each) do - expect(provider).to receive(:revert_snapshot).with(pool, vm, snapshot_name).and_return(true) - end - - it 'should log success messages' do - expect(logger).to receive(:log).with('s', "[ ] [snapshot_manager] 'Attempting to revert #{vm}' in pool #{pool} to snapshot '#{snapshot_name}'") - expect(logger).to receive(:log).with('s', /\[\+\] \[snapshot_manager\] '#{vm}' reverted to snapshot '#{snapshot_name}' in 0.[\d]+ seconds/) - - subject._revert_vm_snapshot(pool, vm, snapshot_name, provider) - end - - it 'should return true' do - expect(subject._revert_vm_snapshot(pool, vm, snapshot_name, provider)).to be true - end - end - - context 'Given a snapshot creation that fails' do - before(:each) do - expect(provider).to receive(:revert_snapshot).with(pool, vm, snapshot_name).and_return(false) - end - - it 'should log failure messages' do - expect(logger).to receive(:log).with('s', "[ ] [snapshot_manager] 'Attempting to revert #{vm}' in pool #{pool} to snapshot '#{snapshot_name}'") - expect(logger).to receive(:log).with('s', "[+] [snapshot_manager] Failed to revert #{vm}' in pool #{pool} to snapshot '#{snapshot_name}'") - - subject._revert_vm_snapshot(pool, vm, snapshot_name, provider) - end - - it 'should return false' do - expect(subject._revert_vm_snapshot(pool, vm, snapshot_name, provider)).to be false - end - end - end - - describe '#get_pool_name_for_vm' do - context 'Given a valid VM' do - before(:each) do - redis_connection_pool.with do |redis| - create_running_vm(pool, vm, redis, token) - end - end - - it 'should return the pool name' do - redis_connection_pool.with do |redis| - expect(subject.get_pool_name_for_vm(vm,redis)).to eq(pool) - end - end - end - - context 'Given an invalid VM' do - it 'should return nil' do - redis_connection_pool.with do |redis| - expect(subject.get_pool_name_for_vm('does_not_exist',redis)).to be_nil - end - end - end - end - - describe '#get_provider_for_pool' do - let(:provider_name) { 'mock_provider' } - - before do - expect(subject).not_to be_nil - # Inject mock provider into global variable - Note this is a code smell - $providers = { provider_name => provider } - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $providers = nil - end - - context 'Given a pool name which does not exist' do - let(:config) { YAML.load(<<-EOT ---- -:config: -:providers: - :mock: -:pools: - - name: '#{pool}' - size: 1 -EOT - )} - - it 'should return nil' do - expect(subject.get_provider_for_pool('pool_does_not_exist')).to be_nil - end - end - - context 'Given a pool which does not have a provider' do - let(:config) { YAML.load(<<-EOT ---- -:config: -:providers: - :mock: -:pools: - - name: '#{pool}' - size: 1 -EOT - )} - - it 'should return nil' do - expect(subject.get_provider_for_pool(pool)).to be_nil - end - end - - context 'Given a pool which uses an invalid provider' do - let(:config) { YAML.load(<<-EOT ---- -:config: -:providers: - :mock: -:pools: - - name: '#{pool}' - size: 1 - provider: 'does_not_exist' -EOT - )} - - it 'should return nil' do - expect(subject.get_provider_for_pool(pool)).to be_nil - end - end - - context 'Given a pool which uses a valid provider' do - let(:config) { YAML.load(<<-EOT ---- -:config: -:providers: - :mock: -:pools: - - name: '#{pool}' - size: 1 - provider: #{provider_name} -EOT - )} - - it 'should return a provider object' do - result = subject.get_provider_for_pool(pool) - expect(result).to_not be_nil - expect(result.name).to eq(provider_name) - end - end - end - - describe '#check_disk_queue' do - let(:threads) {[]} - - before(:each) do - expect(Thread).to receive(:new).and_yield - allow(subject).to receive(:_check_disk_queue) - end - - it 'should log the disk manager is starting' do - expect(logger).to receive(:log).with('d', "[*] [disk_manager] starting worker thread") - - expect($threads.count).to be(0) - subject.check_disk_queue(1,0) - expect($threads.count).to be(1) - end - - it 'should add the manager to the global thread list' do - # Note - Ruby core types are not necessarily thread safe - expect($threads.count).to be(0) - subject.check_disk_queue(1,0) - expect($threads.count).to be(1) - end - - it 'should call _check_disk_queue' do - expect(subject).to receive(:_check_disk_queue).with(no_args) - - subject.check_disk_queue(1,0) - end - - context 'delays between loops' do - let(:maxloop) { 2 } - let(:loop_delay) { 1 } - # Note a maxloop of zero can not be tested as it never terminates - - it 'defaults to 5 second loop delay' do - expect(subject).to receive(:sleep).with(5).exactly(maxloop).times - subject.check_disk_queue(maxloop) - end - - it 'when a non-default loop delay is specified' do - expect(subject).to receive(:sleep).with(loop_delay).exactly(maxloop).times - - subject.check_disk_queue(maxloop,loop_delay) - end - end - - context 'loops specified number of times (5)' do - let(:maxloop) { 5 } - # Note a maxloop of zero can not be tested as it never terminates - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - it 'should call _check_disk_queue 5 times' do - expect(subject).to receive(:_check_disk_queue).with(no_args).exactly(maxloop).times - - subject.check_disk_queue(maxloop,0) - end - end - end - - describe '#_check_disk_queue' do - before do - expect(subject).not_to be_nil - end - - context 'when no VMs in the queue' do - it 'should not call create_vm_disk' do - expect(subject).to receive(:create_vm_disk).exactly(0).times - subject._check_disk_queue - end - end - - context 'when VM in the queue does not exist' do - before(:each) do - redis_connection_pool.with do |redis| - disk_task_vm(vm,"snapshot_#{vm}",redis) - end - end - - it 'should log an error' do - expect(logger).to receive(:log).with('s', /Unable to determine which pool #{vm} is a member of/) - - subject._check_disk_queue - end - - it 'should not call create_vm_disk' do - expect(subject).to receive(:create_vm_disk).exactly(0).times - - subject._check_disk_queue - end - end - - context 'when specified provider does not exist' do - before(:each) do - redis_connection_pool.with do |redis| - disk_task_vm(vm,"snapshot_#{vm}",redis) - create_running_vm(pool, vm, redis, token) - expect(subject).to receive(:get_provider_for_pool).and_return(nil) - end - end - - it 'should log an error' do - expect(logger).to receive(:log).with('s', /Missing Provider for/) - - subject._check_disk_queue - end - - it 'should not call create_vm_disk' do - expect(subject).to receive(:create_vm_disk).exactly(0).times - - subject._check_disk_queue - end - end - - context 'when multiple VMs in the queue' do - before(:each) do - ['vm1', 'vm2', 'vm3'].each do |vm_name| - redis_connection_pool.with do |redis| - disk_task_vm(vm_name,"snapshot_#{vm_name}",redis) - create_running_vm(pool, vm_name, redis, token) - end - end - - allow(subject).to receive(:get_provider_for_pool).with(pool).and_return(provider) - end - - it 'should call create_vm_disk once' do - expect(subject).to receive(:create_vm_disk).exactly(1).times - subject._check_disk_queue - end - - it 'should create the disk for the first VM in the queue' do - expect(subject).to receive(:create_vm_disk).with(pool,'vm1','snapshot_vm1',provider) - subject._check_disk_queue - end - - it 'should log an error if one occurs' do - expect(subject).to receive(:create_vm_disk).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('s', "[!] [disk_manager] disk creation appears to have failed: MockError") - subject._check_disk_queue - end - end - end - - describe '#check_snapshot_queue' do - let(:threads) {[]} - - before(:each) do - expect(Thread).to receive(:new).and_yield - allow(subject).to receive(:_check_snapshot_queue).with(no_args) - end - - it 'should log the snapshot manager is starting' do - expect(logger).to receive(:log).with('d', "[*] [snapshot_manager] starting worker thread") - - expect($threads.count).to be(0) - subject.check_snapshot_queue(1,0) - expect($threads.count).to be(1) - end - - it 'should add the manager to the global thread list' do - # Note - Ruby core types are not necessarily thread safe - expect($threads.count).to be(0) - subject.check_snapshot_queue(1,0) - expect($threads.count).to be(1) - end - - it 'should call _check_snapshot_queue' do - expect(subject).to receive(:_check_snapshot_queue).with(no_args) - - subject.check_snapshot_queue(1,0) - end - - context 'delays between loops' do - let(:maxloop) { 2 } - let(:loop_delay) { 1 } - # Note a maxloop of zero can not be tested as it never terminates - - it 'defaults to 5 second loop delay' do - expect(subject).to receive(:sleep).with(5).exactly(maxloop).times - subject.check_snapshot_queue(maxloop) - end - - it 'when a non-default loop delay is specified' do - expect(subject).to receive(:sleep).with(loop_delay).exactly(maxloop).times - - subject.check_snapshot_queue(maxloop,loop_delay) - end - end - - context 'loops specified number of times (5)' do - let(:maxloop) { 5 } - # Note a maxloop of zero can not be tested as it never terminates - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - it 'should call _check_snapshot_queue 5 times' do - expect(subject).to receive(:_check_snapshot_queue).with(no_args).exactly(maxloop).times - - subject.check_snapshot_queue(maxloop,0) - end - end - end - - describe '#_check_snapshot_queue' do - before do - expect(subject).not_to be_nil - end - - context 'vmpooler__tasks__snapshot queue' do - context 'when no VMs in the queue' do - it 'should not call create_vm_snapshot' do - expect(subject).to receive(:create_vm_snapshot).exactly(0).times - subject._check_snapshot_queue - end - end - - context 'when VM in the queue does not exist' do - before(:each) do - redis_connection_pool.with do |redis| - snapshot_vm(vm,"snapshot_#{vm}",redis) - end - end - - it 'should log an error' do - expect(logger).to receive(:log).with('s', /Unable to determine which pool #{vm} is a member of/) - - subject._check_snapshot_queue - end - - it 'should not call create_vm_snapshot' do - expect(subject).to receive(:create_vm_snapshot).exactly(0).times - - subject._check_snapshot_queue - end - end - - context 'when specified provider does not exist' do - before(:each) do - redis_connection_pool.with do |redis| - snapshot_vm(vm,"snapshot_#{vm}",redis) - create_running_vm(pool, vm, redis, token) - expect(subject).to receive(:get_provider_for_pool).and_return(nil) - end - end - - it 'should log an error' do - expect(logger).to receive(:log).with('s', /Missing Provider for/) - - subject._check_snapshot_queue - end - - it 'should not call create_vm_snapshot' do - expect(subject).to receive(:create_vm_snapshot).exactly(0).times - - subject._check_snapshot_queue - end - end - - context 'when multiple VMs in the queue' do - before(:each) do - ['vm1', 'vm2', 'vm3'].each do |vm_name| - redis_connection_pool.with do |redis| - snapshot_vm(vm_name,"snapshot_#{vm_name}",redis) - create_running_vm(pool, vm_name, redis, token) - end - end - - allow(subject).to receive(:get_provider_for_pool).with(pool).and_return(provider) - end - - it 'should call create_vm_snapshot once' do - expect(subject).to receive(:create_vm_snapshot).exactly(1).times - subject._check_snapshot_queue - end - - it 'should snapshot the first VM in the queue' do - expect(subject).to receive(:create_vm_snapshot).with(pool,'vm1','snapshot_vm1',provider) - subject._check_snapshot_queue - end - - it 'should log an error if one occurs' do - expect(subject).to receive(:create_vm_snapshot).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('s', "[!] [snapshot_manager] snapshot create appears to have failed: MockError") - subject._check_snapshot_queue - end - end - end - - context 'vmpooler__tasks__snapshot-revert queue' do - context 'when no VMs in the queue' do - it 'should not call revert_vm_snapshot' do - expect(subject).to receive(:revert_vm_snapshot).exactly(0).times - subject._check_snapshot_queue - end - end - - context 'when VM in the queue does not exist' do - before(:each) do - redis_connection_pool.with do |redis| - snapshot_revert_vm(vm,"snapshot_#{vm}",redis) - end - end - - it 'should log an error' do - expect(logger).to receive(:log).with('s', /Unable to determine which pool #{vm} is a member of/) - - subject._check_snapshot_queue - end - - it 'should not call revert_vm_snapshot' do - expect(subject).to receive(:revert_vm_snapshot).exactly(0).times - - subject._check_snapshot_queue - end - end - - context 'when specified provider does not exist' do - before(:each) do - redis_connection_pool.with do |redis| - snapshot_revert_vm(vm,"snapshot_#{vm}",redis) - create_running_vm(pool, vm, redis, token) - expect(subject).to receive(:get_provider_for_pool).and_return(nil) - end - end - - it 'should log an error' do - expect(logger).to receive(:log).with('s', /Missing Provider for/) - - subject._check_snapshot_queue - end - - it 'should not call revert_vm_snapshot' do - expect(subject).to receive(:revert_vm_snapshot).exactly(0).times - - subject._check_snapshot_queue - end - end - - context 'when multiple VMs in the queue' do - before(:each) do - ['vm1', 'vm2', 'vm3'].each do |vm_name| - redis_connection_pool.with do |redis| - snapshot_revert_vm(vm_name,"snapshot_#{vm_name}",redis) - create_running_vm(pool, vm_name, redis, token) - end - end - - allow(subject).to receive(:get_provider_for_pool).with(pool).and_return(provider) - end - - it 'should call revert_vm_snapshot once' do - expect(subject).to receive(:revert_vm_snapshot).exactly(1).times - subject._check_snapshot_queue - end - - it 'should snapshot the first VM in the queue' do - expect(subject).to receive(:revert_vm_snapshot).with(pool,'vm1','snapshot_vm1',provider) - subject._check_snapshot_queue - end - - it 'should log an error if one occurs' do - expect(subject).to receive(:revert_vm_snapshot).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('s', "[!] [snapshot_manager] snapshot revert appears to have failed: MockError") - subject._check_snapshot_queue - end - end - end - end - - describe '#migrate_vm' do - before(:each) do - expect(subject).not_to be_nil - expect(Thread).to receive(:new).and_yield - end - - it 'calls migrate_vm' do - expect(provider).to receive(:migrate_vm).with(pool, vm) - - subject.migrate_vm(vm, pool, provider) - end - - context 'When an error is raised' do - before(:each) do - expect(provider).to receive(:migrate_vm).with(pool, vm).and_raise('MockError') - end - - it 'logs a message' do - allow(logger).to receive(:log) - expect(logger).to receive(:log).with('s', "[x] [#{pool}] '#{vm}' migration failed with an error: MockError") - - subject.migrate_vm(vm, pool, provider) - end - end - - context 'with a locked vm mutex' do - let(:mutex) { Mutex.new } - before(:each) do - mutex.lock - end - - it 'should return' do - expect(subject).to receive(:vm_mutex).and_return(mutex) - - expect(subject.migrate_vm(vm, pool, provider)).to be_nil - end - end - end - - describe '#vm_mutex' do - it 'should return a mutex' do - expect(subject.vm_mutex(vm)).to be_a(Mutex) - end - - it 'should return the same mutex when called twice' do - first = subject.vm_mutex(vm) - second = subject.vm_mutex(vm) - expect(first).to be(second) - end - end - - describe '#dereference_mutex' do - it 'should return nil when no mutex is dereferenced' do - expect(subject.dereference_mutex(vm)).to be_nil - end - - it 'should return true when a mutex is dereferenced' do - subject.vm_mutex(vm) - expect(subject.dereference_mutex(vm)).to be true - end - - it 'should dereference the mutex' do - mutex = subject.vm_mutex(vm) - - subject.dereference_mutex(vm) - - result = subject.vm_mutex(vm) - expect(result).to_not eq(mutex) - end - end - - describe 'sync_pool_template' do - let(:old_template) { 'templates/old-template' } - let(:new_template) { 'templates/new-template' } - let(:config) { YAML.load(<<-EOT ---- -:pools: - - name: '#{pool}' - size: 1 - template: old_template -EOT - ) - } - - it 'returns when a template is not set in redis' do - expect(subject.sync_pool_template(config[:pools][0])).to be_nil - end - - it 'returns when a template is set and matches the configured template' do - redis_connection_pool.with do |redis| - redis.hset('vmpooler__config__template', pool, old_template) - end - subject.sync_pool_template(config[:pools][0]) - - expect(config[:pools][0]['template']).to eq(old_template) - end - - it 'updates a pool template when the redis provided value is different' do - redis_connection_pool.with do |redis| - redis.hset('vmpooler__config__template', pool, new_template) - end - - subject.sync_pool_template(config[:pools][0]) - - expect(config[:pools][0]['template']).to eq(new_template) - end - end - - describe 'pool_mutex' do - it 'should return a mutex' do - expect(subject.pool_mutex(pool)).to be_a(Mutex) - end - - it 'should return the same mutex when called twice' do - first = subject.pool_mutex(pool) - second = subject.pool_mutex(pool) - expect(first).to be(second) - end - end - - describe 'update_pool_template' do - let(:current_template) { 'templates/pool_template' } - let(:new_template) { 'templates/new_pool_template' } - let(:config) { - YAML.load(<<-EOT ---- -:config: {} -:pools: - - name: #{pool} - template: "#{current_template}" -EOT - ) - } - let(:poolconfig) { config[:pools][0] } - - before(:each) do - allow(logger).to receive(:log) - end - - it 'should set the pool template to match the configured template' do - redis_connection_pool.with do |redis| - subject.update_pool_template(poolconfig, provider, new_template, current_template, redis) - end - - expect(poolconfig['template']).to eq(new_template) - end - - it 'should log that the template is updated' do - expect(logger).to receive(:log).with('s', "[*] [#{pool}] template updated from #{current_template} to #{new_template}") - - redis_connection_pool.with do |redis| - subject.update_pool_template(poolconfig, provider, new_template, current_template, redis) - end - end - - it 'should run drain_pool' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:drain_pool).with(pool, redis) - - subject.update_pool_template(poolconfig, provider, new_template, current_template, redis) - end - end - - it 'should log that a template is being prepared' do - expect(logger).to receive(:log).with('s', "[*] [#{pool}] preparing pool template for deployment") - - redis_connection_pool.with do |redis| - subject.update_pool_template(poolconfig, provider, new_template, current_template, redis) - end - end - - it 'should run prepare_template' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:prepare_template).with(poolconfig, provider, redis) - - subject.update_pool_template(poolconfig, provider, new_template, current_template, redis) - end - end - - it 'should log that the pool is ready for use' do - expect(logger).to receive(:log).with('s', "[*] [#{pool}] is ready for use") - - redis_connection_pool.with do |redis| - subject.update_pool_template(poolconfig, provider, new_template, current_template, redis) - end - end - end - - describe '#remove_excess_vms' do - let(:config) { - YAML.load(<<-EOT ---- -:pools: - - name: #{pool} - size: 2 -EOT - ) - } - - before(:each) do - expect(subject).not_to be_nil - end - - context 'with a 0 total value' do - let(:ready) { 0 } - let(:total) { 0 } - it 'should return nil' do - expect(subject.remove_excess_vms(config[:pools][0])).to be_nil - end - end - - context 'when the mutex is locked' do - let(:mutex) { Mutex.new } - before(:each) do - redis_connection_pool.with do |redis| - create_ready_vm(pool,'vm1',redis) - create_ready_vm(pool,'vm2',redis) - create_pending_vm(pool,'vm3',redis) - end - mutex.lock - expect(subject).to receive(:pool_mutex).with(pool).and_return(mutex) - end - - it 'should return nil' do - expect(subject.remove_excess_vms(config[:pools][0])).to be_nil - end - end - - context 'with a total size less than the pool size' do - it 'should return nil' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:scard).with("vmpooler__pending__#{pool}").and_return(1) - expect(redis).to receive(:scard).with("vmpooler__ready__#{pool}").and_return(1) - end - expect(subject.remove_excess_vms(config[:pools][0])).to be_nil - end - end - - context 'with a total size greater than the pool size' do - it 'should remove excess ready vms' do - redis_connection_pool.with do |redis| - ['vm1','vm2','vm3','vm4'].each do |v| - create_ready_vm(pool,v,redis) - end - end - expect(subject).to receive(:move_vm_queue).exactly(2).times - - subject.remove_excess_vms(config[:pools][0]) - end - - it 'should remove excess pending vms' do - redis_connection_pool.with do |redis| - create_pending_vm(pool,'vm1',redis) - create_pending_vm(pool,'vm2',redis) - create_ready_vm(pool,'vm3',redis) - create_ready_vm(pool,'vm4',redis) - create_ready_vm(pool,'vm5',redis) - expect(subject).to receive(:move_vm_queue).exactly(3).times - end - - subject.remove_excess_vms(config[:pools][0]) - end - end - end - - describe 'prepare_template' do - let(:config) { YAML.load(<<-EOT ---- -:config: - create_template_delta_disks: true -:providers: - :mock: -:pools: - - name: '#{pool}' - size: 1 - template: 'templates/pool1' -EOT - ) - } - - context 'when creating the template delta disks' do - before(:each) do - redis_connection_pool.with do |redis| - allow(redis).to receive(:hset) - end - allow(provider).to receive(:create_template_delta_disks) - end - - it 'should run create template delta disks' do - redis_connection_pool.with do |redis| - expect(provider).to receive(:create_template_delta_disks).with(config[:pools][0]) - - subject.prepare_template(config[:pools][0], provider, redis) - end - end - - it 'should mark the template as prepared' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hset).with('vmpooler__template__prepared', pool, config[:pools][0]['template']) - - subject.prepare_template(config[:pools][0], provider, redis) - end - end - end - - context 'when template delta disk creation fails' do - before(:each) do - redis_connection_pool.with do |redis| - allow(redis).to receive(:hset) - end - expect(provider).to receive(:create_template_delta_disks).and_raise("MockError") - end - - it 'should log a message when delta disk creation returns an error' do - redis_connection_pool.with do |redis| - expect(logger).to receive(:log).with('s', "[!] [#{pool}] failed while preparing a template with an error. As a result vmpooler could not create the template delta disks. Either a template delta disk already exists, or the template delta disk creation failed. The error is: MockError") - - subject.prepare_template(config[:pools][0], provider, redis) - end - end - end - end - - describe 'evaluate_template' do - let(:mutex) { Mutex.new } - let(:current_template) { 'templates/template1' } - let(:new_template) { 'templates/template2' } - let(:config) { YAML.load(<<-EOT ---- -:config: - task_limit: 5 -:providers: - :mock: -:pools: - - name: '#{pool}' - size: 1 - template: '#{current_template}' -EOT - ) - } - - before(:each) do - redis_connection_pool.with do |redis| - allow(redis).to receive(:hget) - end - expect(subject).to receive(:pool_mutex).with(pool).and_return(mutex) - end - - it 'should retreive the prepared template' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__template__prepared', pool).and_return(current_template) - end - - subject.evaluate_template(config[:pools][0], provider) - end - - it 'should retrieve the redis configured template' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__template', pool).and_return(new_template) - end - - subject.evaluate_template(config[:pools][0], provider) - end - - context 'when the mutex is locked' do - before(:each) do - mutex.lock - end - - it 'should return' do - expect(subject.evaluate_template(config[:pools][0], provider)).to be_nil - end - end - - context 'when prepared template is nil' do - - it 'should prepare the template' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__template__prepared', pool).and_return(nil) - expect(subject).to receive(:prepare_template).with(config[:pools][0], provider, redis) - end - - subject.evaluate_template(config[:pools][0], provider) - end - - it 'should not prepare the template again' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__template__prepared', pool).and_return(current_template) - end - expect(subject).to_not receive(:prepare_template).with(config[:pools][0], provider) - - subject.evaluate_template(config[:pools][0], provider) - end - end - - context 'when the configured pool template does not match the prepared template' do - before(:each) do - redis_connection_pool.with do |redis| - config[:pools][0]['template'] = new_template - expect(redis).to receive(:hget).with('vmpooler__template__prepared', pool).and_return(current_template) - end - end - - it 'should prepare the template' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:prepare_template).with(config[:pools][0], provider, redis) - end - - subject.evaluate_template(config[:pools][0], provider) - end - - context 'if configured_template is provided' do - it 'should not run prepare_template' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__template', pool).and_return(current_template) - end - expect(subject).to_not receive(:prepare_template) - - subject.evaluate_template(config[:pools][0], provider) - end - end - end - - context 'when a new template is requested' do - before(:each) do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__template__prepared', pool).and_return(current_template) - expect(redis).to receive(:hget).with('vmpooler__config__template', pool).and_return(new_template) - end - end - - it 'should update the template' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:update_pool_template).with(config[:pools][0], provider, new_template, current_template, redis) - end - - subject.evaluate_template(config[:pools][0], provider) - end - end - end - - describe 'drain_pool' do - before(:each) do - allow(logger).to receive(:log) - end - - context 'with no vms' do - it 'should return nil' do - redis_connection_pool.with do |redis| - expect(subject.drain_pool(pool, redis)).to be_nil - end - end - - it 'should not log any messages' do - expect(logger).to_not receive(:log) - - redis_connection_pool.with do |redis| - subject.drain_pool(pool, redis) - end - end - - it 'should not try to move any vms' do - expect(subject).to_not receive(:move_vm_queue) - - redis_connection_pool.with do |redis| - subject.drain_pool(pool, redis) - end - end - end - - context 'with ready vms' do - before(:each) do - redis_connection_pool.with do |redis| - create_ready_vm(pool, 'vm1', redis) - create_ready_vm(pool, 'vm2', redis) - end - end - - it 'removes the ready instances' do - expect(subject).to receive(:move_vm_queue).twice - - redis_connection_pool.with do |redis| - subject.drain_pool(pool, redis) - end - end - - it 'logs that ready instances are being removed' do - expect(logger).to receive(:log).with('s', "[*] [#{pool}] removing ready instances") - - redis_connection_pool.with do |redis| - subject.drain_pool(pool, redis) - end - end - end - - context 'with pending instances' do - before(:each) do - redis_connection_pool.with do |redis| - create_pending_vm(pool, 'vm1', redis) - create_pending_vm(pool, 'vm2', redis) - end - end - - it 'removes the pending instances' do - expect(subject).to receive(:move_vm_queue).twice - - redis_connection_pool.with do |redis| - subject.drain_pool(pool, redis) - end - end - - it 'logs that pending instances are being removed' do - expect(logger).to receive(:log).with('s', "[*] [#{pool}] removing pending instances") - - redis_connection_pool.with do |redis| - subject.drain_pool(pool, redis) - end - end - end - end - - describe 'update_pool_size' do - let(:newsize) { '3' } - let(:config) { - YAML.load(<<-EOT ---- -:pools: - - name: #{pool} - size: 2 -EOT - ) - } - let(:poolconfig) { config[:pools][0] } - - context 'with a locked mutex' do - - let(:mutex) { Mutex.new } - before(:each) do - mutex.lock - expect(subject).to receive(:pool_mutex).with(pool).and_return(mutex) - end - - it 'should return nil' do - expect(subject.update_pool_size(poolconfig)).to be_nil - end - end - - it 'should get the pool size configuration from redis' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__poolsize', pool) - end - - subject.update_pool_size(poolconfig) - end - - it 'should return when poolsize is not set in redis' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__poolsize', pool).and_return(nil) - end - - expect(subject.update_pool_size(poolconfig)).to be_nil - end - - it 'should return when no change in configuration is required' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__poolsize', pool).and_return('2') - end - - expect(subject.update_pool_size(poolconfig)).to be_nil - end - - it 'should update the pool size' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__poolsize', pool).and_return(newsize) - end - - subject.update_pool_size(poolconfig) - - expect(poolconfig['size']).to eq(Integer(newsize)) - end - end - - describe 'update_clone_target' do - let(:newtarget) { 'cluster2' } - let(:config) { - YAML.load(<<-EOT ---- -:pools: - - name: #{pool} - clone_target: 'cluster1' -EOT - ) - } - let(:poolconfig) { config[:pools][0] } - - context 'with a locked mutex' do - - let(:mutex) { Mutex.new } - before(:each) do - mutex.lock - expect(subject).to receive(:pool_mutex).with(pool).and_return(mutex) - end - - it 'should return nil' do - expect(subject.update_clone_target(poolconfig)).to be_nil - end - end - - it 'should get the pool clone target configuration from redis' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__clone_target', pool) - end - - subject.update_clone_target(poolconfig) - end - - it 'should return when clone_target is not set in redis' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__clone_target', pool).and_return(nil) - end - - expect(subject.update_clone_target(poolconfig)).to be_nil - end - - it 'should return when no change in configuration is required' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__clone_target', pool).and_return('cluster1') - end - - expect(subject.update_clone_target(poolconfig)).to be_nil - end - - it 'should update the clone target' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hget).with('vmpooler__config__clone_target', pool).and_return(newtarget) - end - - subject.update_clone_target(poolconfig) - - expect(poolconfig['clone_target']).to eq(newtarget) - end - end - - describe "#execute!" do - let(:config) { - YAML.load(<<-EOT ---- -:pools: - - name: #{pool} -EOT - ) - } - - before(:each) do - expect(subject).not_to be_nil - - allow(subject).to receive(:check_disk_queue) - allow(subject).to receive(:check_snapshot_queue) - allow(subject).to receive(:check_pool) - allow(subject).to receive(:check_ondemand_requests) - - allow(logger).to receive(:log) - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - context 'on startup' do - it 'should log a message that VMPooler has started' do - expect(logger).to receive(:log).with('d', 'starting vmpooler') - - subject.execute!(1,0) - end - - it 'should set clone tasks to zero' do - redis_connection_pool.with do |redis| - redis.set('vmpooler__tasks__clone', 1) - subject.execute!(1,0) - expect(redis.get('vmpooler__tasks__clone')).to eq('0') - end - end - - it 'should clear migration tasks' do - redis_connection_pool.with do |redis| - redis.set('vmpooler__migration', 1) - subject.execute!(1,0) - expect(redis.get('vmpooler__migration')).to be_nil - end - end - - it 'should run the check_disk_queue method' do - expect(subject).to receive(:check_disk_queue) - - subject.execute!(1,0) - end - - it 'should run the check_snapshot_queue method' do - expect(subject).to receive(:check_snapshot_queue) - - subject.execute!(1,0) - end - - it 'should check the pools in the config' do - expect(subject).to receive(:check_pool).with(a_pool_with_name_of(pool)) - - subject.execute!(1,0) - end - - it 'should run the check_ondemand_requests method' do - expect(subject).to receive(:check_ondemand_requests) - - subject.execute!(1,0) - end - - context 'creating Providers' do - let(:vsphere_provider) { double('vsphere_provider') } - let(:config) { - YAML.load(<<-EOT ---- -:providers: - :vsphere: {} -:pools: - - name: #{pool} - provider: 'vsphere' - - name: 'vsphere-pool-2' - provider: 'vsphere' -EOT - )} - - it 'should call create_provider_object idempotently' do - # Even though there are two pools using the vsphere provider, it should only - # create the provider object once. - expect(subject).to receive(:create_provider_object).with(Object, Object, Object, redis_connection_pool, 'vsphere', 'vsphere', Object).and_return(vsphere_provider) - - subject.execute!(1,0) - end - - it 'should raise an error if the provider can not be created' do - expect(subject).to receive(:create_provider_object).and_raise(RuntimeError, "MockError") - - expect{ subject.execute!(1,0) }.to raise_error(/MockError/) - end - - it 'should log a message if the provider can not be created' do - expect(subject).to receive(:create_provider_object).and_raise(RuntimeError, "MockError") - expect(logger).to receive(:log).with('s',"Error while creating provider for pool #{pool}: MockError") - - expect{ subject.execute!(1,0) }.to raise_error(/MockError/) - end - end - end - - context 'creating multiple vsphere Providers' do - let(:vsphere_provider) { double('vsphere_provider') } - let(:vsphere_provider2) { double('vsphere_provider') } - let(:provider1) { Vmpooler::PoolManager::Provider::Base.new(config, logger, metrics, redis_connection_pool, 'vsphere', provider_options) } - let(:provider2) { Vmpooler::PoolManager::Provider::Base.new(config, logger, metrics, redis_connection_pool, 'secondvsphere', provider_options) } - let(:config) { - YAML.load(<<-EOT ---- -:providers: - :vsphere: - server: 'blah1' - provider_class: 'vsphere' - :secondvsphere: - server: 'blah2' - provider_class: 'vsphere' -:pools: - - name: #{pool} - provider: 'vsphere' - - name: 'secondpool' - provider: 'secondvsphere' -EOT - )} - - it 'should call create_provider_object twice' do - # The two pools use a different provider name, but each provider_class is vsphere - expect(subject).to receive(:create_provider_object).with(Object, Object, Object, redis_connection_pool, "vsphere", "vsphere", Object).and_return(vsphere_provider) - expect(subject).to receive(:create_provider_object).with(Object, Object, Object, redis_connection_pool, "vsphere", "secondvsphere", Object).and_return(vsphere_provider2) - subject.execute!(1,0) - end - - it 'should have vsphere providers with different servers' do - allow(subject).to receive(:get_provider_for_pool).with(pool).and_return(provider1) - result = subject.get_provider_for_pool(pool) - allow(provider1).to receive(:provider_config).and_call_original - expect(result.provider_config['server']).to eq('blah1') - - allow(subject).to receive(:get_provider_for_pool).with('secondpool').and_return(provider2) - result = subject.get_provider_for_pool('secondpool') - allow(provider1).to receive(:provider_config).and_call_original - expect(result.provider_config['server']).to eq('blah2') - subject.execute!(1,0) - end - end - - context 'modify configuration on startup' do - context 'move vSphere configuration to providers location' do - let(:config) { - YAML.load(<<-EOT -:vsphere: - server: 'vsphere.example.com' - username: 'vmpooler' - password: 'password' -:pools: - - name: #{pool} -EOT - )} - - it 'should set providers with no provider to vsphere' do - expect(subject.config[:providers]).to be nil - - subject.execute!(1,0) - expect(subject.config[:providers][:vsphere]['server']).to eq('vsphere.example.com') - expect(subject.config[:providers][:vsphere]['username']).to eq('vmpooler') - expect(subject.config[:providers][:vsphere]['password']).to eq('password') - end - - it 'should log a message' do - expect(logger).to receive(:log).with('d', "[!] Detected an older configuration file. Copying the settings from ':vsphere:' to ':providers:/:vsphere:'") - - subject.execute!(1,0) - end - end - end - - context 'with dead disk_manager thread' do - let(:disk_manager_thread) { double('thread', :alive? => false) } - - before(:each) do - # Reset the global variable - Note this is a code smell - $threads = {} - $threads['disk_manager'] = disk_manager_thread - end - - it 'should run the check_disk_queue method and log a message' do - expect(subject).to receive(:check_disk_queue) - expect(logger).to receive(:log).with('d', "[!] [disk_manager] worker thread died, restarting") - - subject.execute!(1,0) - end - end - - context 'with dead snapshot_manager thread' do - let(:snapshot_manager_thread) { double('thread', :alive? => false) } - before(:each) do - # Reset the global variable - Note this is a code smell - $threads = {} - $threads['snapshot_manager'] = snapshot_manager_thread - end - - it 'should run the check_snapshot_queue method and log a message' do - expect(subject).to receive(:check_snapshot_queue) - expect(logger).to receive(:log).with('d', "[!] [snapshot_manager] worker thread died, restarting") - $threads['snapshot_manager'] = snapshot_manager_thread - - subject.execute!(1,0) - end - end - - context 'with dead pool thread' do - context 'without check_loop_delay_xxx settings' do - let(:pool_thread) { double('thread', :alive? => false) } - let(:default_check_loop_delay_min) { 5 } - let(:default_check_loop_delay_max) { 60 } - let(:default_check_loop_delay_decay) { 2.0 } - before(:each) do - # Reset the global variable - Note this is a code smell - $threads = {} - $threads[pool] = pool_thread - end - - it 'should run the check_pool method and log a message' do - expect(subject).to receive(:check_pool).with(a_pool_with_name_of(pool), - default_check_loop_delay_min, - default_check_loop_delay_max, - default_check_loop_delay_decay) - expect(logger).to receive(:log).with('d', "[!] [#{pool}] worker thread died, restarting") - - subject.execute!(1,0) - end - end - - context 'with dead ondemand provisioner thread' do - let(:ondemand_provisioner_thread) { double('thread', :alive? => false) } - let(:default_check_loop_delay_min) { 5 } - let(:default_check_loop_delay_max) { 60 } - let(:default_check_loop_delay_decay) { 2.0 } - before(:each) do - # Reset the global variable - Note this is a code smell - $threads = {} - $threads['ondemand_provisioner'] = ondemand_provisioner_thread - end - - it 'should run the process_ondemand_requests method' do - expect(subject).to receive(:check_ondemand_requests).with(default_check_loop_delay_min, default_check_loop_delay_max, default_check_loop_delay_decay) - subject.execute!(1,0) - end - - end - - context 'with check_loop_delay_xxx settings' do - let(:pool_thread) { double('thread', :alive? => false) } - let(:check_loop_delay_min) { 7 } - let(:check_loop_delay_max) { 20 } - let(:check_loop_delay_decay) { 2.1 } - - let(:config) { - YAML.load(<<-EOT ---- -:config: - check_loop_delay_min: #{check_loop_delay_min} - check_loop_delay_max: #{check_loop_delay_max} - check_loop_delay_decay: #{check_loop_delay_decay} -:pools: - - name: #{pool} -EOT - ) - } - before(:each) do - # Reset the global variable - Note this is a code smell - $threads = {} - $threads[pool] = pool_thread - end - - it 'should run the check_pool method and log a message' do - expect(subject).to receive(:check_pool).with(a_pool_with_name_of(pool), - check_loop_delay_min, - check_loop_delay_max, - check_loop_delay_decay) - expect(logger).to receive(:log).with('d', "[!] [#{pool}] worker thread died, restarting") - - subject.execute!(1,0) - end - end - end - - context 'delays between loops' do - let(:maxloop) { 2 } - let(:loop_delay) { 1 } - # Note a maxloop of zero can not be tested as it never terminates - before(:each) do - - allow(subject).to receive(:check_disk_queue) - allow(subject).to receive(:check_snapshot_queue) - allow(subject).to receive(:check_pool) - allow(subject).to receive(:check_ondemand_requests) - end - - it 'when a non-default loop delay is specified' do - expect(subject).to receive(:sleep).with(loop_delay).exactly(maxloop).times - - subject.execute!(maxloop,loop_delay) - end - end - - context 'loops specified number of times (5)' do - let(:alive_thread) { double('thread', :alive? => true) } - let(:maxloop) { 5 } - # Note a maxloop of zero can not be tested as it never terminates - before(:each) do - end - - it 'should run startup tasks only once' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:set).with('vmpooler__tasks__clone', 0).once - expect(redis).to receive(:set).with('vmpooler__tasks__ondemandclone', 0).once - expect(redis).to receive(:del).with('vmpooler__migration').once - end - - subject.execute!(maxloop,0) - end - - it 'should run per thread tasks 5 times when threads are not remembered' do - expect(subject).to receive(:check_disk_queue).exactly(maxloop).times - expect(subject).to receive(:check_snapshot_queue).exactly(maxloop).times - expect(subject).to receive(:check_pool).exactly(maxloop).times - expect(subject).to receive(:check_ondemand_requests).exactly(maxloop).times - - subject.execute!(maxloop,0) - end - - it 'should not run per thread tasks when threads are alive' do - expect(subject).to receive(:check_disk_queue).exactly(0).times - expect(subject).to receive(:check_snapshot_queue).exactly(0).times - expect(subject).to receive(:check_pool).exactly(0).times - expect(subject).to receive(:check_ondemand_requests).exactly(0).times - - $threads[pool] = alive_thread - $threads['disk_manager'] = alive_thread - $threads['snapshot_manager'] = alive_thread - $threads['ondemand_provisioner'] = alive_thread - - subject.execute!(maxloop,0) - end - end - - context 'when redis server connection is not available' do - let(:maxloop) { 2 } - it 'should log a failure and raise the error' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:set).with('vmpooler__tasks__clone', 0).and_raise(Redis::CannotConnectError) - end - expect(logger).to receive(:log).with('s', 'Cannot connect to the redis server: Redis::CannotConnectError') - - expect{subject.execute!(maxloop,0)}.to raise_error Redis::CannotConnectError - end - end - end - - describe "#sleep_with_wakeup_events" do - let(:loop_delay) { 10 } - before(:each) do - allow(Kernel).to receive(:sleep).and_raise("sleep should not be called") - allow(subject).to receive(:time_passed?).with(:wakeup_by, Time).and_call_original - allow(subject).to receive(:time_passed?).with(:exit_by, Time).and_call_original - end - - it 'should not sleep if the loop delay is negative' do - expect(subject).to receive(:sleep).exactly(0).times - - subject.sleep_with_wakeup_events(-1) - end - - it 'should sleep until the loop delay is exceeded' do - # This test is a little brittle as it requires knowledge of the implementation - # Basically the number of sleep delays will dictate how often the time_passed? method is called - - expect(subject).to receive(:sleep).exactly(2).times - expect(subject).to receive(:time_passed?).with(:exit_by, Time).and_return(false, false, false, true) - - subject.sleep_with_wakeup_events(loop_delay) - end - - describe 'with the pool_size_change wakeup option' do - let(:wakeup_option) {{ - :pool_size_change => true, - :poolname => pool, - }} - let(:wakeup_period) { -1 } # A negative number forces the wakeup evaluation to always occur - - it 'should check the number of VMs ready in Redis' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:time_passed?).with(:exit_by, Time).and_return(false, true) - expect(redis).to receive(:scard).with("vmpooler__ready__#{pool}").once - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - - it 'should sleep until the number of VMs ready in Redis increases' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:scard).with("vmpooler__ready__#{pool}").and_return(1,1,1,2) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - - it 'should sleep until the number of VMs ready in Redis decreases' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:scard).with("vmpooler__ready__#{pool}").and_return(2,2,2,1) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - end - - describe 'with the pool_template_change wakeup option' do - let(:wakeup_option) {{ - :pool_template_change => true, - :poolname => pool - }} - let(:new_template) { 'templates/newtemplate' } - let(:wakeup_period) { -1 } # A negative number forces the wakeup evaluation to always occur - - context 'with a template configured' do - before(:each) do - redis_connection_pool.with do |redis| - redis.hset('vmpooler__config__template', pool, new_template) - allow(redis).to receive(:hget) - end - end - - it 'should check if a template is configured in redis' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:time_passed?).with(:exit_by, Time).and_return(false, true) - expect(redis).to receive(:hget).with('vmpooler__template__prepared', pool).once - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - - it 'should sleep until a template change is detected' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:hget).with('vmpooler__config__template', pool).and_return(nil,nil,new_template) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - end - end - - describe 'with the pool_reset wakeup option' do - let(:wakeup_option) {{ - :pool_reset => true, - :poolname => pool - }} - - let(:wakeup_period) { -1 } # A negative number forces the wakeup evaluation to always occur - - context 'when a pool reset is requested' do - - it 'should sleep until the reset request is detected' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:sismember).with('vmpooler__poolreset', pool).and_return(false,false,true) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - end - end - - describe 'with the undo_override wakeup option' do - let(:wakeup_option) {{ - :undo_override => true, - :poolname => pool - }} - - let(:wakeup_period) { -1 } # A negative number forces the wakeup evaluation to always occur - - context 'when a undoing a template override is requested' do - before(:each) do - redis_connection_pool.with do |redis| - redis.sadd('vmpooler__pool__undo_template_override', pool) - allow(redis).to receive(:hget) - end - end - - it 'should sleep until the undo override request is detected' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).at_least(2).times - expect(subject).to receive(:sleep).at_most(3).times - expect(redis).to receive(:sismember).with('vmpooler__pool__undo_template_override', pool).and_return(false,false,true) - expect(redis).to receive(:sismember).with('vmpooler__pool__undo_size_override', pool).and_return(false,false) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - end - - context 'when a undoing a size override is requested' do - before(:each) do - redis_connection_pool.with do |redis| - redis.sadd('vmpooler__pool__undo_size_override', pool) - allow(redis).to receive(:hget) - end - end - - it 'should sleep until the undo override request is detected' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:sismember).with('vmpooler__pool__undo_template_override', pool).and_return(false,false,false) - expect(redis).to receive(:sismember).with('vmpooler__pool__undo_size_override', pool).and_return(false,false,true) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - end - end - - describe 'with the pending_vm wakeup option' do - let(:wakeup_option) {{ - :pending_vm => true, - :poolname => pool - }} - - let(:wakeup_period) { -1 } # A negative number forces the wakeup evaluation to always occur - - context 'when a pending_vm is detected' do - - it 'should sleep until the pending instance' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:scard).with("vmpooler__pending__#{pool}").and_return(0,0,1) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - end - end - - describe 'with the ondemand_request wakeup option' do - let(:wakeup_option) {{ :ondemand_request => true }} - - let(:wakeup_period) { -1 } # A negative number forces the wakeup evaluation to always occur - - it 'should sleep until the provisioning request is detected' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:multi).and_return('OK').exactly(3).times - expect(redis).to receive(:exec).and_return([0,0,0],[0,0,0],[1,0,0]) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - - it 'should sleep until provisioning processing is detected' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:multi).and_return('OK').exactly(3).times - expect(redis).to receive(:exec).and_return([0,0,0],[0,0,0],[0,1,0]) - end - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - - it 'should sleep until ondemand creation task is detected' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:sleep).exactly(3).times - expect(redis).to receive(:multi).and_return('OK').exactly(3).times - expect(redis).to receive(:exec).and_return([0,0,0],[0,0,0],[0,0,1]) - end - - subject.sleep_with_wakeup_events(loop_delay, wakeup_period, wakeup_option) - end - end - end - - describe "#check_pool" do - let(:threads) {{}} - let(:provider_name) { 'mock_provider' } - let(:config) { - YAML.load(<<-EOT ---- -:pools: - - name: #{pool} - provider: #{provider_name} -EOT - ) - } - - let(:pool_object) { config[:pools][0] } - let(:check_pool_response) {{ - :discovered_vms => 0, - :checked_running_vms => 0, - :checked_ready_vms => 0, - :checked_pending_vms => 0, - :destroyed_vms => 0, - :migrated_vms => 0, - :cloned_vms => 0, - }} - - before do - expect(subject).not_to be_nil - expect(Thread).to receive(:new).and_yield - allow(subject).to receive(:get_provider_for_pool).with(pool).and_return(provider) - end - - context 'on startup' do - before(:each) do - allow(subject).to receive(:_check_pool).and_return(check_pool_response) - expect(logger).to receive(:log).with('d', "[*] [#{pool}] starting worker thread") - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - it 'should log a message the worker thread is starting' do - subject.check_pool(pool_object,1,0) - end - - it 'should populate the threads global variable' do - subject.check_pool(pool_object,1,0) - - # Unable to test for nil as the Thread is mocked - expect($threads.keys.include?(pool)) - end - end - - context 'delays between loops' do - let(:maxloop) { 2 } - let(:loop_delay) { 1 } - # Note a maxloop of zero can not be tested as it never terminates - - before(:each) do - allow(logger).to receive(:log) - allow(subject).to receive(:_check_pool).and_return(check_pool_response) - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - it 'when a non-default loop delay is specified' do - expect(subject).to receive(:sleep_with_wakeup_events).with(loop_delay, Numeric, Hash).exactly(maxloop).times - - subject.check_pool(pool_object,maxloop,loop_delay,loop_delay) - end - - it 'specifies a wakeup option for pool size change' do - expect(subject).to receive(:sleep_with_wakeup_events).with(loop_delay, Numeric, hash_including(:pool_size_change => true)).exactly(maxloop).times - - subject.check_pool(pool_object,maxloop,loop_delay,loop_delay) - end - - it 'specifies a wakeup option for poolname' do - expect(subject).to receive(:sleep_with_wakeup_events).with(loop_delay, Numeric, hash_including(:poolname => pool)).exactly(maxloop).times - - subject.check_pool(pool_object,maxloop,loop_delay,loop_delay) - end - end - - context 'when redis connection fails' do - let(:maxloop) { 2 } - let(:loop_delay) { 1 } - - it 'should raise the error' do - allow(subject).to receive(:_check_pool).and_raise(Redis::CannotConnectError) - expect(logger).to receive(:log).with('d', "[*] [#{pool}] starting worker thread") - - expect{subject.check_pool(pool_object,maxloop,loop_delay,loop_delay)}.to raise_error Redis::CannotConnectError - end - end - - context 'delays between loops configured in the pool configuration' do - let(:maxloop) { 2 } - let(:loop_delay) { 1 } - let(:pool_loop_delay) { 2 } - let(:config) { - YAML.load(<<-EOT ---- -:pools: - - name: #{pool} - provider: #{provider_name} - check_loop_delay_min: #{pool_loop_delay} - check_loop_delay_max: #{pool_loop_delay} -EOT - ) - } - - before(:each) do - allow(logger).to receive(:log) - allow(subject).to receive(:_check_pool).and_return(check_pool_response) - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - it 'when a non-default loop delay is specified' do - expect(subject).to receive(:sleep_with_wakeup_events).with(pool_loop_delay, pool_loop_delay, Hash).exactly(maxloop).times - - subject.check_pool(pool_object,maxloop,loop_delay) - end - end - - context 'delays between loops with a specified min and max value' do - let(:maxloop) { 5 } - let(:loop_delay_min) { 1 } - let(:loop_delay_max) { 60 } - # Note a maxloop of zero can not be tested as it never terminates - - before(:each) do - allow(logger).to receive(:log) - allow(subject).to receive(:_check_pool).and_return(check_pool_response) - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - [:checked_pending_vms, :discovered_vms, :cloned_vms].each do |testcase| - describe "when #{testcase} is greater than zero" do - it "delays the minimum delay time" do - expect(subject).to receive(:sleep_with_wakeup_events).with(loop_delay_min, loop_delay_min, Hash).exactly(maxloop).times - check_pool_response[testcase] = 1 - - subject.check_pool(pool_object,maxloop,loop_delay_min,loop_delay_max) - end - end - end - - [:checked_running_vms, :checked_ready_vms, :destroyed_vms, :migrated_vms].each do |testcase| - describe "when #{testcase} is greater than zero" do - let(:loop_decay) { 3.0 } - it "delays increases with a decay" do - expect(subject).to receive(:sleep_with_wakeup_events).with(3, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(9, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(27, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(60, Numeric, Hash).twice - check_pool_response[testcase] = 1 - - subject.check_pool(pool_object,maxloop,loop_delay_min,loop_delay_max,loop_decay) - end - end - end - end - - context 'delays between loops with a specified min and max value configured in the pool configuration' do - let(:maxloop) { 5 } - let(:loop_delay_min) { 1 } - let(:loop_delay_max) { 60 } - let(:loop_decay) { 3.0 } - let(:pool_loop_delay_min) { 3 } - let(:pool_loop_delay_max) { 70 } - let(:pool_loop_delay_decay) { 2.5 } - let(:config) { - YAML.load(<<-EOT ---- -:pools: - - name: #{pool} - provider: #{provider_name} - check_loop_delay_min: #{pool_loop_delay_min} - check_loop_delay_max: #{pool_loop_delay_max} - check_loop_delay_decay: #{pool_loop_delay_decay} -EOT - ) - } - - before(:each) do - allow(logger).to receive(:log) - allow(subject).to receive(:_check_pool).and_return(check_pool_response) - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - end - - [:checked_pending_vms, :discovered_vms, :cloned_vms].each do |testcase| - describe "when #{testcase} is greater than zero" do - it "delays the minimum delay time" do - expect(subject).to receive(:sleep_with_wakeup_events).with(pool_loop_delay_min, Numeric, Hash).exactly(maxloop).times - check_pool_response[testcase] = 1 - - subject.check_pool(pool_object,maxloop,loop_delay_min,loop_delay_max,loop_decay) - end - end - end - - [:checked_running_vms, :checked_ready_vms, :destroyed_vms, :migrated_vms].each do |testcase| - describe "when #{testcase} is greater than zero" do - it "delays increases with a decay" do - expect(subject).to receive(:sleep_with_wakeup_events).with(7, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(17, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(42, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(70, Numeric, Hash).twice - check_pool_response[testcase] = 1 - - subject.check_pool(pool_object,maxloop,loop_delay_min,loop_delay_max,loop_decay) - end - end - end - end - - - context 'loops specified number of times (5)' do - let(:maxloop) { 5 } - # Note a maxloop of zero can not be tested as it never terminates - before(:each) do - allow(logger).to receive(:log) - allow(subject).to receive(:_check_pool).and_return(check_pool_response) - end - - after(:each) do - # Reset the global variable - Note this is a code smell - $threads = nil - $provider = nil - end - - it 'should run startup tasks only once' do - expect(logger).to receive(:log).with('d', "[*] [#{pool}] starting worker thread") - - subject.check_pool(pool_object,maxloop,0) - end - - it 'should run per thread tasks 5 times' do - expect(subject).to receive(:_check_pool).exactly(maxloop).times - - subject.check_pool(pool_object,maxloop,0) - end - end - end - - describe 'undo_override' do - let(:mutex) { Mutex.new } - let(:original_template) { 'templates/template1' } - let(:override_template) { 'templates/template2' } - let(:original_size) { 2 } - let(:override_size) { 10 } - let(:config) { YAML.load(<<-EOT ---- -:config: - task_limit: 5 -:providers: - :mock: -:pools: - - name: '#{pool}' - size: #{override_size} - template: '#{override_template}' -:pool_index: - '#{pool}': 0 -:pools_at_startup: - - name: '#{pool}' - size: #{original_size} - template: '#{original_template}' -EOT - ) - } - - before(:each) do - redis_connection_pool.with do |redis| - redis.sadd('vmpooler__pool__undo_template_override', pool) - redis.sadd('vmpooler__pool__undo_size_override', pool) - # allow(redis).to receive(:hget) - end - end - - it 'should revert to the original template and pool size' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:sismember).with('vmpooler__pool__undo_template_override', pool).and_return(true) - expect(redis).to receive(:srem).with('vmpooler__pool__undo_template_override', pool).and_return(true) - expect(subject).to receive(:update_pool_template).with(config[:pools][0], provider, original_template, override_template, redis) - - expect(redis).to receive(:sismember).with('vmpooler__pool__undo_size_override', pool).and_return(true) - expect(redis).to receive(:srem).with('vmpooler__pool__undo_size_override', pool).and_return(true) - end - - subject.undo_override(config[:pools][0], provider) - end - end - - describe '#create_inventory' do - - it 'should log an error if one occurs' do - allow(provider).to receive(:vms_in_pool).and_raise( - RuntimeError,'Mock Error' - ) - - expect { - subject.create_inventory(config[:pools].first, provider, {}) - }.to raise_error(RuntimeError, 'Mock Error') - end - end - - describe '#check_running_pool_vms' do - let(:pool_object) { config[:pools][0] } - let(:pool_check_response) { - {:checked_running_vms => 0} - } - context 'Running VM not in the inventory' do - let(:inventory) { - # mock response from create_inventory - {} - } - before(:each) do - redis_connection_pool.with do |redis| - create_running_vm(pool,vm,redis,token) - end - end - - it 'should not call check_running_vm' do - expect(subject).to receive(:check_running_vm).exactly(0).times - - subject.check_running_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should move the VM to completed queue' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:move_vm_queue).with(pool,vm,'running','completed',redis,String).and_call_original - end - - subject.check_running_pool_vms(pool,provider, pool_check_response, inventory) - end - end - - context 'Running VM in the inventory' do - let(:provider) { double('provider') } - let(:inventory) { - # mock response from create_inventory - { vm => 1 } - } - before(:each) do - redis_connection_pool.with do |redis| - allow(subject).to receive(:check_running_vm) - create_running_vm(pool,vm,redis,token) - end - end - - it 'should log an error if one occurs' do - expect(subject).to receive(:check_running_vm).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('d', "[!] [#{pool}] _check_pool with an error while evaluating running VMs: MockError") - - subject.check_running_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should return the number of checked running VMs' do - subject.check_running_pool_vms(pool, provider, pool_check_response, inventory) - - expect(pool_check_response[:checked_running_vms]).to be(1) - end - - it 'should use the VM lifetime in preference to defaults' do - big_lifetime = 2000 - redis_connection_pool.with do |redis| - redis.hset("vmpooler__vm__#{vm}", 'lifetime',big_lifetime) - end - # The lifetime comes in as string - expect(subject).to receive(:check_running_vm).with(vm,pool,"#{big_lifetime}",provider) - - subject.check_running_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should use the configuration default if the VM lifetime is not set' do - config[:config]['vm_lifetime'] = 50 - expect(subject).to receive(:check_running_vm).with(vm,pool,50,provider) - - subject.check_running_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should use a lifetime of 12 if nothing is set' do - expect(subject).to receive(:check_running_vm).with(vm,pool,12,provider) - - subject.check_running_pool_vms(pool, provider, pool_check_response, inventory) - end - end - end - - describe '#check_ready_pool_vms' do - let(:provider) { double('provider') } - let(:pool_ttl) { 5 } - let(:pool_check_response) { - {:checked_ready_vms => 0} - } - - context 'Ready VM not in the inventory' do - let(:inventory) { - # mock response from create_inventory - {} - } - before(:each) do - redis_connection_pool.with do |redis| - create_ready_vm(pool,vm,redis,token) - end - end - - it 'should not call check_ready_vm' do - expect(subject).to receive(:check_ready_vm).exactly(0).times - - subject.check_ready_pool_vms(pool, provider, pool_check_response, inventory, pool_ttl) - end - - it 'should move the VM to completed queue' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:move_vm_queue).with(pool,vm,'ready','completed',redis,String).and_call_original - end - - subject.check_ready_pool_vms(pool, provider, pool_check_response, inventory, pool_ttl) - end - end - - context 'Ready VM in the inventory' do - let(:inventory) { - # mock response from create_inventory - {vm => 1} - } - let(:big_lifetime) { 2000 } - before(:each) do - redis_connection_pool.with do |redis| - allow(subject).to receive(:check_ready_vm) - create_ready_vm(pool,vm,redis,token) - end - end - - it 'should return the number of checked ready VMs' do - subject.check_ready_pool_vms(pool, provider, pool_check_response, inventory, pool_ttl) - - expect(pool_check_response[:checked_ready_vms]).to be(1) - end - - it 'should log an error if one occurs' do - expect(subject).to receive(:check_ready_vm).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('d', "[!] [#{pool}] _check_pool failed with an error while evaluating ready VMs: MockError") - - subject.check_ready_pool_vms(pool, provider, pool_check_response, inventory, big_lifetime) - end - - it 'should use the pool TTL if set' do - expect(subject).to receive(:check_ready_vm).with(vm,pool,big_lifetime,provider) - - subject.check_ready_pool_vms(pool, provider, pool_check_response, inventory, big_lifetime) - end - - it 'should use a pool TTL of zero if none set' do - expect(subject).to receive(:check_ready_vm).with(vm,pool,pool_ttl,provider) - - subject.check_ready_pool_vms(pool, provider, pool_check_response, inventory, pool_ttl) - end - end - end - - describe '#check_pending_pool_vms' do - let(:provider) { double('provider') } - let(:timeout) { 10 } - let(:pool_check_response) { - {:checked_pending_vms => 0} - } - - context 'Pending VM not in the inventory' do - let(:inventory) { - # mock response from create_inventory - {} - } - - before(:each) do - redis_connection_pool.with do |redis| - create_pending_vm(pool,vm,redis,token) - end - end - - it 'should call fail_pending_vm' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:fail_pending_vm).with(vm, pool, Integer, redis, exists: false) - end - - subject.check_pending_pool_vms(pool, provider, pool_check_response, inventory, timeout) - end - end - - context 'Pending VM in the inventory' do - let(:inventory) { - # mock response from create_inventory - {vm => 1} - } - - before(:each) do - redis_connection_pool.with do |redis| - create_pending_vm(pool,vm,redis,token) - end - end - - it 'should return the number of checked pending VMs' do - allow(subject).to receive(:check_pending_vm) - subject.check_pending_pool_vms(pool, provider, pool_check_response, inventory, timeout) - - expect(pool_check_response[:checked_pending_vms]).to be(1) - end - - it 'should log an error if one occurs' do - expect(subject).to receive(:check_pending_vm).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('d', "[!] [#{pool}] _check_pool failed with an error while evaluating pending VMs: MockError") - - subject.check_pending_pool_vms(pool, provider, pool_check_response, inventory, timeout) - end - - it 'should use the pool timeout if set' do - big_lifetime = 2000 - - config[:pools][0]['timeout'] = big_lifetime - expect(subject).to receive(:check_pending_vm).with(vm,pool,big_lifetime,provider) - - subject.check_pending_pool_vms(pool, provider, pool_check_response, inventory, big_lifetime) - end - - it 'should use the configuration setting if the pool timeout is not set' do - big_lifetime = 2000 - - config[:config]['timeout'] = big_lifetime - expect(subject).to receive(:check_pending_vm).with(vm,pool,big_lifetime,provider) - - subject.check_pending_pool_vms(pool, provider, pool_check_response, inventory, big_lifetime) - end - - it 'should use a pool timeout of 15 if nothing is set' do - expect(subject).to receive(:check_pending_vm).with(vm,pool,timeout,provider) - - subject.check_pending_pool_vms(pool, provider, pool_check_response, inventory, timeout) - end - end - end - - describe '#check_completed_pool_vms' do - let(:provider) { double('provider') } - let(:pool_check_response) { { - :checked_completed_vms => 0, - :destroyed_vms => 0 - } - } - context 'Completed VM not in the inventory' do - let(:inventory) { - # mock response from create_inventory - {} - } - - before(:each) do - redis_connection_pool.with do |redis| - create_completed_vm(vm,pool,redis,true) - end - end - - it 'should log a message' do - subject.check_completed_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should not call destroy_vm' do - expect(subject).to receive(:destroy_vm).exactly(0).times - - subject.check_completed_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should remove redis information' do - redis_connection_pool.with do |redis| - expect(redis.sismember("vmpooler__completed__#{pool}",vm)).to be(true) - expect(redis.hget("vmpooler__vm__#{vm}", 'checkout')).to_not be(nil) - expect(redis.hget("vmpooler__active__#{pool}",vm)).to_not be(nil) - - subject.check_completed_pool_vms(pool, provider, pool_check_response, inventory) - - expect(redis.sismember("vmpooler__completed__#{pool}",vm)).to be(false) - expect(redis.hget("vmpooler__vm__#{vm}", 'checkout')).to be(nil) - expect(redis.hget("vmpooler__active__#{pool}",vm)).to be(nil) - end - end - end - - context 'Completed VM in the inventory' do - let(:inventory) { - # mock response from create_inventory - {vm => 1} - } - - before(:each) do - redis_connection_pool.with do |redis| - create_completed_vm(vm,pool,redis,true) - end - end - - it 'should call destroy_vm' do - expect(subject).to receive(:destroy_vm) - - subject.check_completed_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should return the number of destroyed VMs' do - subject.check_completed_pool_vms(pool, provider, pool_check_response, inventory) - - expect(pool_check_response[:destroyed_vms]).to be(1) - end - - context 'with an error during destroy_vm' do - before(:each) do - expect(subject).to receive(:destroy_vm).and_raise(RuntimeError,"MockError") - expect(logger).to receive(:log).with('d', "[!] [#{pool}] _check_pool failed with an error while evaluating completed VMs: MockError") - end - - it 'should log a message' do - subject.check_completed_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should remove redis information' do - redis_connection_pool.with do |redis| - expect(redis.sismember("vmpooler__completed__#{pool}",vm)).to be(true) - expect(redis.hget("vmpooler__vm__#{vm}", 'checkout')).to_not be(nil) - expect(redis.hget("vmpooler__active__#{pool}",vm)).to_not be(nil) - - subject.check_completed_pool_vms(pool, provider, pool_check_response, inventory) - - expect(redis.sismember("vmpooler__completed__#{pool}",vm)).to be(false) - expect(redis.hget("vmpooler__vm__#{vm}", 'checkout')).to be(nil) - expect(redis.hget("vmpooler__active__#{pool}",vm)).to be(nil) - end - end - end - end - end - - describe "#check_discovered_pool_vms" do - context 'Discovered VM' do - before(:each) do - redis_connection_pool.with do |redis| - create_discovered_vm(vm,pool,redis) - end - end - - it 'should be moved to the completed queue' do - subject.check_discovered_pool_vms(pool) - - redis_connection_pool.with do |redis| - expect(redis.sismember("vmpooler__completed__#{pool}", vm)).to be(true) - end - end - - it 'should log a message if an error occurs' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:smove).with("vmpooler__discovered__#{pool}", "vmpooler__completed__#{pool}", vm).and_raise(RuntimeError,'MockError') - end - expect(logger).to receive(:log).with("d", "[!] [#{pool}] _check_pool failed with an error while evaluating discovered VMs: MockError") - - subject.check_discovered_pool_vms(pool) - end - - ['pending','ready','running','completed'].each do |queue_name| - context "exists in the #{queue_name} queue" do - before(:each) do - allow(subject).to receive(:migrate_vm) - allow(subject).to receive(:check_running_vm) - allow(subject).to receive(:check_ready_vm) - allow(subject).to receive(:check_pending_vm) - allow(subject).to receive(:destroy_vm) - allow(subject).to receive(:clone_vm) - end - - it "should remain in the #{queue_name} queue" do - redis_connection_pool.with do |redis| - redis.sadd("vmpooler__#{queue_name}__#{pool}", vm) - allow(logger).to receive(:log) - - subject.check_discovered_pool_vms(pool) - - expect(redis.sismember("vmpooler__#{queue_name}__#{pool}", vm)).to be(true) - end - end - - it "should be removed from the discovered queue" do - redis_connection_pool.with do |redis| - redis.sadd("vmpooler__#{queue_name}__#{pool}", vm) - allow(logger).to receive(:log) - - expect(redis.sismember("vmpooler__discovered__#{pool}", vm)).to be(true) - subject.check_discovered_pool_vms(pool) - expect(redis.sismember("vmpooler__discovered__#{pool}", vm)).to be(false) - end - end - - it "should log a message" do - redis_connection_pool.with do |redis| - redis.sadd("vmpooler__#{queue_name}__#{pool}", vm) - end - expect(logger).to receive(:log).with('d', "[!] [#{pool}] '#{vm}' found in '#{queue_name}', removed from 'discovered' queue") - - subject.check_discovered_pool_vms(pool) - end - end - end - end - end - - describe "#check_migrating_pool_vms" do - let(:provider) { double('provider') } - let(:pool_check_response) { { - :migrated_vms => 0 - } - } - - context 'Migrating VM not in the inventory' do - let(:inventory) { - # mock response from create_inventory - {} - } - - before(:each) do - redis_connection_pool.with do |redis| - create_migrating_vm(vm,pool,redis) - end - end - - it 'should not do anything' do - expect(subject).to receive(:migrate_vm).exactly(0).times - - subject.check_migrating_pool_vms(pool, provider, pool_check_response, inventory) - end - end - - context 'Migrating VM in the inventory' do - let(:inventory) { - # mock response from create_inventory - {vm => 1} - } - - before(:each) do - redis_connection_pool.with do |redis| - create_migrating_vm(vm,pool,redis) - end - end - - it 'should return the number of migrated VMs' do - allow(subject).to receive(:migrate_vm).with(vm,pool,provider) - subject.check_migrating_pool_vms(pool, provider, pool_check_response, inventory) - - expect(pool_check_response[:migrated_vms]).to be(1) - end - - it 'should log an error if one occurs' do - expect(subject).to receive(:migrate_vm).and_raise(RuntimeError,'MockError') - expect(logger).to receive(:log).with('s', "[x] [#{pool}] '#{vm}' failed to migrate: MockError") - - subject.check_migrating_pool_vms(pool, provider, pool_check_response, inventory) - end - - it 'should call migrate_vm' do - expect(subject).to receive(:migrate_vm).with(vm,pool,provider) - - subject.check_migrating_pool_vms(pool, provider, pool_check_response, inventory) - end - end - end - - describe '#repopulate_pool_vms' do - let(:pool_size) { 1 } - let(:config) { - YAML.load(<<-EOT ---- -:config: - task_limit: 10 -EOT - ) - } - let(:provider) { double('provider') } - let(:pool_check_response) { - { - :cloned_vms => 0 - } - } - - it 'should not call clone_vm when number of VMs is equal to the pool size' do - expect(subject).to receive(:clone_vm).exactly(0).times - - subject.repopulate_pool_vms(pool, provider, pool_check_response, 0) - end - - it 'should not call clone_vm when number of VMs is greater than the pool size' do - redis_connection_pool.with do |redis| - create_ready_vm(pool,vm,redis,token) - end - expect(subject).to receive(:clone_vm).exactly(0).times - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - - ['ready','pending'].each do |queue_name| - it "should use VMs in #{queue_name} queue to calculate pool size" do - expect(subject).to receive(:clone_vm).exactly(0).times - # Modify the pool size to 1 and add a VM in the queue - redis_connection_pool.with do |redis| - redis.sadd("vmpooler__#{queue_name}__#{pool}",vm) - end - pool_size = 1 - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - end - - ['running','completed','discovered','migrating'].each do |queue_name| - it "should not use VMs in #{queue_name} queue to calculate pool size" do - expect(subject).to receive(:clone_vm) - # Modify the pool size to 1 and add a VM in the queue - redis_connection_pool.with do |redis| - redis.sadd("vmpooler__#{queue_name}__#{pool}",vm) - end - pool_size = 1 - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - end - - it 'should log a message the first time a pool is empty' do - expect(logger).to receive(:log).with('s', "[!] [#{pool}] is empty") - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - - it 'should not log a message when the pool size is 0' do - expect(logger).to_not receive(:log).with('s', "[!] [#{pool}] is empty") - - subject.repopulate_pool_vms(pool, provider, pool_check_response, 0) - end - - context 'when pool is marked as empty' do - - before(:each) do - redis_connection_pool.with do |redis| - redis.set("vmpooler__empty__#{pool}", 'true') - end - end - - it 'should not log a message when the pool remains empty' do - expect(logger).to receive(:log).with('s', "[!] [#{pool}] is empty").exactly(0).times - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - - it 'should remove the empty pool mark if it is no longer empty' do - redis_connection_pool.with do |redis| - create_ready_vm(pool,vm,redis,token) - - expect(redis.get("vmpooler__empty__#{pool}")).to be_truthy - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - expect(redis.get("vmpooler__empty__#{pool}")).to be_falsey - end - end - end - - context 'when number of VMs is less than the pool size' do - - it 'should return the number of cloned VMs' do - pool_size = 5 - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - - expect(pool_check_response[:cloned_vms]).to be(pool_size) - end - - it 'should call clone_vm to populate the pool' do - pool_size = 5 - - expect(subject).to receive(:clone_vm).exactly(pool_size).times - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - - it 'should call clone_vm until task_limit is hit' do - task_limit = 2 - pool_size = 5 - config[:config]['task_limit'] = task_limit - - expect(subject).to receive(:clone_vm).exactly(task_limit).times - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - - it 'log a message if a cloning error occurs' do - redis_connection_pool.with do |redis| - create_ready_vm(pool,'vm',redis) - end - pool_size = 2 - - expect(subject).to receive(:clone_vm).and_raise(RuntimeError,"MockError") - expect(logger).to receive(:log).with("s", "[!] [#{pool}] clone failed during check_pool with an error: MockError") - expect{ subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) }.to raise_error(RuntimeError,'MockError') - - end - end - - context 'when a pool template is updating' do - let(:poolsize) { 2 } - let(:mutex) { Mutex.new } - before(:each) do - expect(subject).to receive(:pool_mutex).with(pool).and_return(mutex) - mutex.lock - end - - it 'should not call clone_vm to populate the pool' do - expect(subject).to_not receive(:clone_vm) - - subject.repopulate_pool_vms(pool, provider, pool_check_response, poolsize) - end - end - - context 'export metrics' do - it 'increments metrics for ready queue' do - redis_connection_pool.with do |redis| - create_ready_vm(pool,'vm1',redis) - create_ready_vm(pool,'vm2',redis) - create_ready_vm(pool,'vm3',redis) - end - - expect(metrics).to receive(:gauge).with("ready.#{pool}", 3) - allow(metrics).to receive(:gauge) - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - - it 'increments metrics for running queue' do - redis_connection_pool.with do |redis| - create_running_vm(pool,'vm1',redis,token) - create_running_vm(pool,'vm2',redis,token) - create_running_vm(pool,'vm3',redis,token) - end - - expect(metrics).to receive(:gauge).with("running.#{pool}", 3) - allow(metrics).to receive(:gauge) - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - - it 'increments metrics with 0 when pool empty' do - - allow(metrics).to receive(:gauge) - expect(metrics).to receive(:gauge).with("ready.#{pool}", 0) - expect(metrics).to receive(:gauge).with("running.#{pool}", 0) - - subject.repopulate_pool_vms(pool, provider, pool_check_response, pool_size) - end - end - end - - describe '#_check_pool' do - let(:new_vm_response) { - # Mock response from Base Provider for vms_in_pool - [{ 'name' => new_vm}] - } - let(:vm_response) { - # Mock response from Base Provider for vms_in_pool - [{ 'name' => vm}] - } - let(:multi_vm_response) { - # Mock response from Base Provider for vms_in_pool - [{ 'name' => 'vm1'}, - { 'name' => 'vm2'}, - { 'name' => 'vm3'}] - } - - # Default test fixtures will consist of; - # - Empty Redis dataset - # - A single pool with a pool size of zero i.e. no new VMs should be created - # - Task limit of 10 - let(:config) { - YAML.load(<<-EOT ---- -:config: - task_limit: 10 -:pools: - - name: #{pool} - size: 10 - ready_ttl: 1234 - timeout: 5678 -EOT - ) - } - let(:pool_object) { config[:pools][0] } - let(:new_vm) { 'newvm'} - let(:pool_name) { pool_object['name'] } - let(:mutex) { Mutex.new } - - before do - expect(subject).not_to be_nil - allow(logger).to receive(:log) - end - - # INVENTORY - context 'Conducting inventory' do - before(:each) do - allow(subject).to receive(:migrate_vm) - allow(subject).to receive(:check_running_vm) - allow(subject).to receive(:check_ready_vm) - allow(subject).to receive(:check_pending_vm) - allow(subject).to receive(:destroy_vm) - allow(subject).to receive(:clone_vm) - allow(provider).to receive(:vms_in_pool).with(pool).and_return(new_vm_response) - end - - it 'calls inventory correctly' do - expect(subject).to receive(:create_inventory) - subject._check_pool(pool_object, provider) - end - - it 'captures #create_inventory errors correctly' do - allow(subject).to receive(:create_inventory).and_raise(RuntimeError,'Mock Error') - subject._check_pool(pool_object, provider) - end - - it 'should return early if an error occurs' do - allow(subject).to receive(:create_inventory).and_raise( - RuntimeError,'Mock Error' - ) - - expect(subject).to_not receive(:check_running_pool_vms) - subject._check_pool(pool_object, provider) - end - - it 'should return that no actions were taken' do - expect(provider).to receive(:vms_in_pool).and_raise(RuntimeError,'Mock Error') - - result = subject._check_pool(pool_object,provider) - - expect(result[:discovered_vms]).to be(0) - expect(result[:checked_running_vms]).to be(0) - expect(result[:checked_ready_vms]).to be(0) - expect(result[:checked_pending_vms]).to be(0) - expect(result[:destroyed_vms]).to be(0) - expect(result[:migrated_vms]).to be(0) - expect(result[:cloned_vms]).to be(0) - end - - it 'should log the discovery of VMs' do - expect(logger).to receive(:log).with('s', "[?] [#{pool}] '#{new_vm}' added to 'discovered' queue") - - subject._check_pool(pool_object,provider) - end - - it 'should return the number of discovered VMs' do - result = subject._check_pool(pool_object,provider) - - expect(result[:discovered_vms]).to be(1) - end - - it 'should add undiscovered VMs to the completed queue' do - redis_connection_pool.with do |redis| - allow(logger).to receive(:log).with('s', "[?] [#{pool}] '#{new_vm}' added to 'discovered' queue") - - expect(redis.sismember("vmpooler__discovered__#{pool}", new_vm)).to be(false) - expect(redis.sismember("vmpooler__completed__#{pool}", new_vm)).to be(false) - - subject._check_pool(pool_object,provider) - - expect(redis.sismember("vmpooler__discovered__#{pool}", new_vm)).to be(false) - expect(redis.sismember("vmpooler__completed__#{pool}", new_vm)).to be(true) - end - end - - ['running','ready','pending','completed','discovered','migrating'].each do |queue_name| - it "should not discover VMs in the #{queue_name} queue" do - redis_connection_pool.with do |redis| - expect(logger).to receive(:log).with('s', "[?] [#{pool}] '#{new_vm}' added to 'discovered' queue").exactly(0).times - expect(redis.sismember("vmpooler__discovered__#{pool}", new_vm)).to be(false) - redis.sadd("vmpooler__#{queue_name}__#{pool}", new_vm) - - subject._check_pool(pool_object,provider) - - if queue_name == 'discovered' - # Discovered VMs end up in the completed queue - expect(redis.sismember("vmpooler__completed__#{pool}", new_vm)).to be(true) - else - expect(redis.sismember("vmpooler__#{queue_name}__#{pool}", new_vm)).to be(true) - end - end - end - end - - it 'should get the pool mutex' do - expect(subject).to receive(:pool_mutex).and_return(mutex).at_least(:once) - - subject._check_pool(pool_object,provider) - end - - it 'should run synchronize' do - expect(subject).to receive(:pool_mutex).and_return(mutex).at_least(:once) - expect(mutex).to receive(:synchronize).at_least(:once) - - subject._check_pool(pool_object,provider) - end - - it 'should yield when locked' do - expect(subject).to receive(:pool_mutex).and_return(mutex).at_least(:once) - mutex.lock - expect(mutex).to receive(:synchronize).and_yield - - subject._check_pool(pool_object,provider) - end - end - - # RUNNING - context 'when checking running VMs' do - let(:pool_check_response) { - { - discovered_vms: 0, - checked_running_vms: 0, - checked_ready_vms: 0, - checked_pending_vms: 0, - destroyed_vms: 0, - migrated_vms: 0, - cloned_vms: 0 - } - } - - it 'should call #check_running_pool_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:check_running_pool_vms).with(pool, provider, pool_check_response, {}) - - subject._check_pool(pool_object,provider) - end - end - - # READY - context 'when checking ready VMs' do - let(:pool_check_response) { - { - discovered_vms: 0, - checked_running_vms: 0, - checked_ready_vms: 0, - checked_pending_vms: 0, - destroyed_vms: 0, - migrated_vms: 0, - cloned_vms: 0 - } - } - - it 'should call #check_ready_pool_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:check_ready_pool_vms).with(pool, provider, pool_check_response, {}, pool_object['ready_ttl']) - - subject._check_pool(pool_object,provider) - end - end - - # PENDING - context 'when checking ready VMs' do - let(:pool_check_response) { - { - discovered_vms: 0, - checked_running_vms: 0, - checked_ready_vms: 0, - checked_pending_vms: 0, - destroyed_vms: 0, - migrated_vms: 0, - cloned_vms: 0 - } - } - - it 'should call #check_ready_pool_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:check_pending_pool_vms).with(pool, provider, pool_check_response, {}, pool_object['timeout']) - - subject._check_pool(pool_object,provider) - end - end - - - # COMPLETED - context 'when checking completed VMs' do - let(:pool_check_response) { - { - discovered_vms: 0, - checked_running_vms: 0, - checked_ready_vms: 0, - checked_pending_vms: 0, - destroyed_vms: 0, - migrated_vms: 0, - cloned_vms: 0 - } - } - - it 'should call #check_completed_pool_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:check_completed_pool_vms).with(pool, provider, pool_check_response, {}) - - subject._check_pool(pool_object,provider) - end - end - - # DISCOVERED - context 'when checking discovered VMs' do - let(:pool_check_response) { - { - discovered_vms: 0, - checked_running_vms: 0, - checked_ready_vms: 0, - checked_pending_vms: 0, - destroyed_vms: 0, - migrated_vms: 0, - cloned_vms: 0 - } - } - - it 'should call #check_discovered_pool_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:check_discovered_pool_vms).with(pool) - - subject._check_pool(pool_object,provider) - end - end - - # MIGRATIONS - context 'when checking migrating VMs' do - let(:pool_check_response) { - { - discovered_vms: 0, - checked_running_vms: 0, - checked_ready_vms: 0, - checked_pending_vms: 0, - destroyed_vms: 0, - migrated_vms: 0, - cloned_vms: 0 - } - } - - it 'should call #check_migrating_pool_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:check_migrating_pool_vms).with(pool, provider, pool_check_response, {}) - - subject._check_pool(pool_object,provider) - end - end - - # update_pool_size - context 'when a pool size configuration change is detected' do - let(:poolsize) { 2 } - let(:newpoolsize) { 3 } - before(:each) do - config[:pools][0]['size'] = poolsize - redis_connection_pool.with do |redis| - redis.hset('vmpooler__config__poolsize', pool, newpoolsize) - end - end - - it 'should change the pool size configuration' do - allow(subject).to receive(:create_inventory).and_return({}) - - expect(subject).to receive(:update_pool_size).and_call_original - - subject._check_pool(config[:pools][0],provider) - - expect(config[:pools][0]['size']).to be(newpoolsize) - end - end - - #REPOPULATE - context 'when checking if pools need to be repopulated' do - let(:pool_check_response) { { - discovered_vms: 0, - checked_running_vms: 0, - checked_ready_vms: 0, - checked_pending_vms: 0, - destroyed_vms: 0, - migrated_vms: 0, - cloned_vms: 0 - } } - it 'should call #repopulate_pool_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:repopulate_pool_vms).with(pool, provider, pool_check_response, config[:pools][0]['size']) - - subject._check_pool(pool_object, provider) - end - end - - #remove_excess_vms - context 'when an excess number of ready vms exist' do - - before(:each) do - redis_connection_pool.with do |redis| - allow(redis).to receive(:scard) - expect(redis).to receive(:scard).with("vmpooler__ready__#{pool}").and_return(1) - expect(redis).to receive(:scard).with("vmpooler__pending__#{pool}").and_return(1) - end - end - - it 'should call remove_excess_vms' do - allow(subject).to receive(:create_inventory).and_return({}) - expect(subject).to receive(:remove_excess_vms).with(config[:pools][0]) - - subject._check_pool(config[:pools][0],provider) - end - end - end - - describe 'process_ondemand_requests' do - context 'with no requests' do - it 'returns 0' do - result = subject.process_ondemand_requests - expect(result).to eq(0) - end - - it 'runs process_ondemand_vms' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:process_ondemand_vms).with(redis).and_return(0) - subject.process_ondemand_requests - end - end - - it 'checks ready requests' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:check_ondemand_requests_ready).with(redis).and_return(0) - subject.process_ondemand_requests - end - end - end - - context 'with provisioning requests' do - before(:each) do - redis_connection_pool.with do |redis| - redis.zadd('vmpooler__provisioning__request', current_time, request_id) - end - end - - it 'returns the number of requests processed' do - result = subject.process_ondemand_requests - expect(result).to eq(1) - end - - it 'runs create_ondemand_vms for each request' do - redis_connection_pool.with do |redis| - expect(subject).to receive(:create_ondemand_vms).with(request_id, redis) - subject.process_ondemand_requests - end - end - end - end - - describe '#vms_ready?' do - let(:request_string) { "#{pool}:#{pool}:5" } - let(:platform_alias) { pool } - before(:each) do - redis_connection_pool.with do |redis| - create_ondemand_request_for_test(request_id, current_time.to_i, request_string, redis) - end - end - - it 'fails if the request is not valid' do - redis_connection_pool.with do |redis| - request_id = "#{request_id}-wrong" - create_ondemand_request_for_test(request_id, current_time.to_i, "#{pool}:5", redis) - expect{subject.vms_ready?(request_id, redis)}.to raise_error(ArgumentError) - end - end - - it 'returns false when vms for request_id are not ready' do - redis_connection_pool.with do |redis| - result = subject.vms_ready?(request_id, redis) - expect(result).to be false - end - end - - context 'with a request that has all instances ready' do - before(:each) do - redis_connection_pool.with do |redis| - ['vm1','vm2','vm3','vm4','vm5'].each do |v| - redis.sadd("vmpooler__#{request_id}__#{platform_alias}__#{pool}", v) - end - end - end - - it 'returns true' do - redis_connection_pool.with do |redis| - result = subject.vms_ready?(request_id, redis) - expect(result).to be true - end - end - end - - context 'with a request that has some instances ready' do - let(:request_string) { "#{pool}:#{pool}:3,#{pool}2:#{pool}2:3" } - before(:each) do - redis_connection_pool.with do |redis| - create_ondemand_request_for_test(request_id, current_time.to_i, request_string, redis) - ['vm1','vm2','vm3'].each do |v| - redis.sadd("vmpooler__#{request_id}__#{platform_alias}__#{pool}", v) - end - end - end - - it 'returns false' do - redis_connection_pool.with do |redis| - result = subject.vms_ready?(request_id, redis) - expect(result).to be false - end - end - end - end - - describe '#check_ondemand_requests_ready' do - before(:each) do - config[:config]['ondemand_request_ttl'] = 5 - end - - it 'returns 0 when no provisoning requests are in progress' do - redis_connection_pool.with do |redis| - result = subject.check_ondemand_requests_ready(redis) - expect(result).to eq(0) - end - end - - context 'with requests in progress' do - before(:each) do - redis_connection_pool.with do |redis| - create_ondemand_processing(request_id, current_time, redis) - end - end - - it 'returns the number of requests processed' do - expect(subject).to receive(:vms_ready?).and_return(false) - redis_connection_pool.with do |redis| - result = subject.check_ondemand_requests_ready(redis) - expect(result).to eq(1) - end - end - end - end - - describe '#check_ondemand_request_ready' do - let(:score) { current_time.to_f } - before(:each) do - config[:config]['ondemand_request_ttl'] = 5 - end - - context 'when the request is ready' do - before(:each) do - expect(subject).to receive(:vms_ready?).and_return(true) - redis_connection_pool.with do |redis| - expect(redis).to receive(:zscore).and_return(score) - end - end - - it 'sets the request as ready' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:hset).with("vmpooler__odrequest__#{request_id}", 'status', 'ready') - subject.check_ondemand_request_ready(request_id, redis) - end - end - - it 'marks the ondemand request hash key for expiration in one month' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:expire).with("vmpooler__odrequest__#{request_id}", 2592000) - subject.check_ondemand_request_ready(request_id, redis) - end - end - - it 'removes the request from processing' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:zrem).with('vmpooler__provisioning__processing', request_id) - subject.check_ondemand_request_ready(request_id, redis) - end - end - end - - context 'with the score provided' do - it 'should not request the score' do - redis_connection_pool.with do |redis| - expect(redis).to_not receive(:zscore) - expect(subject).to receive(:vms_ready?).and_return(true) - expect(redis).to receive(:zrem).with('vmpooler__provisioning__processing', request_id) - subject.check_ondemand_request_ready(request_id, redis, score) - end - end - end - - context 'when a request has taken too long to be filled' do - it 'should return true for request_expired?' do - redis_connection_pool.with do |redis| - expect(redis).to receive(:zscore).and_return(score) - expect(subject).to receive(:request_expired?).with(request_id, Float, redis).and_return(true) - subject.check_ondemand_request_ready(request_id, redis) - end - end - end - end - - describe 'check_ondemand_requests' do - let(:threads) {[]} - let(:maxloop) { 0 } - let(:loop_delay_min) { 5 } - let(:loop_delay_max) { 60 } - let(:loop_delay_decay) { 2.0 } - - before(:each) do - expect(Thread).to receive(:new).and_yield - end - - it 'should log the ondemand provisioner is starting' do - expect(subject).to receive(:_check_ondemand_requests).with(maxloop, loop_delay_min, loop_delay_max, loop_delay_decay) - expect(logger).to receive(:log).with('d', "[*] [ondemand_provisioner] starting worker thread") - - expect($threads.count).to be(0) - subject.check_ondemand_requests - expect($threads.count).to be(1) - end - - context' delays between loops' do - let(:maxloop) { 2 } - let(:loop_delay) { 1 } - - it 'when a non-default loop delay is specified' do - expect(subject).to receive(:sleep_with_wakeup_events).with(loop_delay, Numeric, Hash).exactly(maxloop).times - - subject.check_ondemand_requests(maxloop,loop_delay,loop_delay) - end - end - - context 'delays between loops with a specified min and max value' do - let(:maxloop) { 5 } - let(:loop_delay_min) { 1 } - let(:loop_delay_max) { 60 } - let(:loop_decay) { 3.0 } - - it 'delay values increase with a decay' do - expect(subject).to receive(:sleep_with_wakeup_events).with(3, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(9, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(27, Numeric, Hash).once - expect(subject).to receive(:sleep_with_wakeup_events).with(60, Numeric, Hash).twice - - subject.check_ondemand_requests(maxloop,loop_delay_min,loop_delay_max,loop_decay) - end - end - - context 'loops specified number of times (5)' do - let(:maxloop) { 5 } - - it 'should run startup tasks only once' do - expect(logger).to receive(:log).with('d', "[*] [ondemand_provisioner] starting worker thread") - - subject.check_ondemand_requests(maxloop,0) - end - - it 'should run per thread tasks 5 times' do - expect(subject).to receive(:process_ondemand_requests).and_return(0).exactly(maxloop).times - - subject.check_ondemand_requests(maxloop,0) - end - end - end -end From 08bc1ed8145ac1b689c5d849c2eff489a34a6929 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 22 Feb 2023 11:31:22 -0500 Subject: [PATCH 39/70] Added mock info for guest ip address, fix existing tests, add dns config --- spec/rbvmomi_helper.rb | 24 +++++++++++++++++-- spec/unit/providers/vsphere_spec.rb | 36 +++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/spec/rbvmomi_helper.rb b/spec/rbvmomi_helper.rb index e0acbaf..ace62b6 100644 --- a/spec/rbvmomi_helper.rb +++ b/spec/rbvmomi_helper.rb @@ -198,13 +198,24 @@ MockVirtualDiskManager = Object MockVirtualMachine = Struct.new( # https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.VirtualMachine.html # From VirtualMachine - :config, :runtime, :snapshot, :summary, + :config, :runtime, :snapshot, :summary, :guest, # From ManagedEntity :name, # From RbVmomi::VIM::ManagedEntity # https://github.com/vmware/rbvmomi/blob/master/lib/rbvmomi/vim/ManagedEntity.rb :path -) +) do + # From RbVmomi::VIM::VirtualMachine + # https://github.com/ManageIQ/rbvmomi2/blob/59a5904031a0d7558f306e8d2a05001f9b6822de/lib/rbvmomi/vim/VirtualMachine.rb#L20 + def guest_ip + g = self.guest + if g.ipAddress + g.ipAddress + else + nil + end + end +end MockVirtualMachineSnapshot = Struct.new( # https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.vm.Snapshot.html @@ -347,6 +358,12 @@ MockVirtualMachineGuestSummary = Struct.new( :hostName ) +MockVirtualMachineGuestInfo = Struct.new( + # https://developer.vmware.com/apis/1311/vsphere + # From Data Object - GuestInfo(vim.vm.GuestInfo) + :ipAddress +) + MockVirtualMachineRuntimeInfo = Struct.new( # https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.vm.RuntimeInfo.html # From VirtualMachineRuntimeInfo @@ -801,6 +818,7 @@ end def mock_RbVmomi_VIM_VirtualMachine(options = {}) options[:snapshot_tree] = nil if options[:snapshot_tree].nil? options[:name] = 'VM' + rand(65536).to_s if options[:name].nil? + options[:ip] = '169.254.255.255' if options[:ip].nil? options[:path] = [] if options[:path].nil? mock = MockVirtualMachine.new() @@ -810,11 +828,13 @@ def mock_RbVmomi_VIM_VirtualMachine(options = {}) mock.summary.runtime = MockVirtualMachineRuntimeInfo.new() mock.summary.guest = MockVirtualMachineGuestSummary.new() mock.runtime = mock.summary.runtime + mock.guest = MockVirtualMachineGuestInfo.new() mock.name = options[:name] mock.summary.guest.hostName = options[:hostname] mock.runtime.bootTime = options[:boottime] mock.runtime.powerState = options[:powerstate] + mock.guest.ipAddress = options[:ip] unless options[:snapshot_tree].nil? mock.snapshot = MockVirtualMachineSnapshotInfo.new() diff --git a/spec/unit/providers/vsphere_spec.rb b/spec/unit/providers/vsphere_spec.rb index 14d9097..2dbc22e 100644 --- a/spec/unit/providers/vsphere_spec.rb +++ b/spec/unit/providers/vsphere_spec.rb @@ -50,6 +50,11 @@ describe 'Vmpooler::PoolManager::Provider::VSphere' do :config: max_tries: 3 retry_factor: 10 +:dns_configs: + :gcp-clouddns: + project: vmpooler-test + domain: vmpooler.example.com + dns_zone_resource_name: vmpooler-example-com :providers: :vsphere: server: "vcenter.domain.local" @@ -71,6 +76,7 @@ describe 'Vmpooler::PoolManager::Provider::VSphere' do ready_ttl: 1440 clone_target: 'cluster1' provider: 'vsphere' + dns_config: 'gcp-clouddns' EOT ) } @@ -535,7 +541,7 @@ EOT end end - context 'when VM exists but is missing information' do + context 'when VM exists but is missing hostname, boottime, powerstate' do let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname, }) @@ -560,10 +566,25 @@ EOT end end + context 'when VM exists but is missing ip' do + let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ + :name => vmname, + :ip => '', + }) + } + + it 'should return empty for ip' do + result = subject.get_vm(poolname,vmname) + + expect(result['ip']).to eq('') + end + end + context 'when VM exists and contains all information' do let(:vm_hostname) { "#{vmname}.demo.local" } let(:boot_time) { Time.now } let(:power_state) { 'MockPowerState' } + let(:ip) { '169.254.255.255' } let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname, @@ -622,6 +643,11 @@ EOT expect(result['powerstate']).to eq(power_state) end + it 'should return the ip' do + result = subject.get_vm(poolname,vmname) + + expect(result['ip']).to eq(ip) + end end end @@ -1056,14 +1082,15 @@ EOT end describe '#vm_ready?' do - let(:domain) { nil } + let(:domain) { 'vmpooler.example.com' } context 'When a VM is ready' do before(:each) do + allow(subject).to receive(:domain).and_return('vmpooler.example.com') expect(subject).to receive(:open_socket).with(vmname, domain) end it 'should return true' do - expect(subject.vm_ready?(poolname,vmname)).to be true + expect(subject.vm_ready?(poolname, vmname)).to be true end end @@ -1075,6 +1102,7 @@ EOT end it 'should return true' do + allow(subject).to receive(:domain).and_return('vmpooler.example.com') expect(subject.vm_ready?('missing_pool',vmname)).to be true end end @@ -1390,7 +1418,7 @@ EOT let(:TCPSocket) { double('tcpsocket') } let(:socket) { double('tcpsocket') } let(:hostname) { 'host' } - let(:domain) { 'domain.local'} + let(:domain) { 'vmpooler.example.com' } let(:default_socket) { 22 } before do From 69e501b93e18a26b3e05f217ea444532b177c1d9 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Tue, 28 Mar 2023 17:59:37 -0400 Subject: [PATCH 40/70] Bump vmpooler requirement and add install gemfile script --- Gemfile.lock | 9 ++++----- install-gemfile-lock | 7 +++++++ vmpooler-provider-vsphere.gemspec | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100755 install-gemfile-lock diff --git a/Gemfile.lock b/Gemfile.lock index 481fbcb..aea3199 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ PATH specs: vmpooler-provider-vsphere (2.1.0) rbvmomi2 (>= 3.1, < 4.0) - vmpooler (~> 2.4) + vmpooler (~> 3.0) GEM remote: https://rubygems.org/ @@ -14,7 +14,7 @@ GEM climate_control (1.2.0) coderay (1.1.3) concurrent-ruby (1.2.2) - connection_pool (2.3.0) + connection_pool (2.4.0) deep_merge (1.2.2) diff-lcs (1.5.0) docile (1.4.0) @@ -146,7 +146,7 @@ GEM thrift (0.18.1) tilt (2.1.0) unicode-display_width (2.4.2) - vmpooler (2.4.0) + vmpooler (3.0.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) deep_merge (~> 1.2) @@ -172,7 +172,6 @@ GEM rspec (~> 3) PLATFORMS - universal-java-1.8 universal-java-11 DEPENDENCIES @@ -188,4 +187,4 @@ DEPENDENCIES yarjuf (>= 2.0) BUNDLED WITH - 2.2.29 + 2.4.10 diff --git a/install-gemfile-lock b/install-gemfile-lock new file mode 100755 index 0000000..07cbd49 --- /dev/null +++ b/install-gemfile-lock @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment +docker run -it --rm \ + -v $(pwd):/app \ + jruby:9.4.1.0-jdk11 \ + /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends make git netbase && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' diff --git a/vmpooler-provider-vsphere.gemspec b/vmpooler-provider-vsphere.gemspec index 1575ae2..8773653 100644 --- a/vmpooler-provider-vsphere.gemspec +++ b/vmpooler-provider-vsphere.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |s| s.files = Dir[ "lib/**/*" ] s.require_paths = ["lib"] s.add_dependency 'rbvmomi2', '>= 3.1', '< 4.0' - s.add_dependency 'vmpooler', '~> 2.4' + s.add_dependency 'vmpooler', '~> 3.0' # Testing dependencies s.add_development_dependency 'climate_control', '>= 0.2.0' From 09c80138cab8022c5bafe3dc45e6c8f3c2616326 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Tue, 18 Apr 2023 19:45:30 -0400 Subject: [PATCH 41/70] 3.0.0 release prep --- CHANGELOG.md | 8 ++++++++ Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ffb9ef..258cd22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [3.0.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.0.0) (2023-04-19) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/2.1.0...3.0.0) + +**Breaking changes:** + +- \(RE-15124\) Collect VMs IP for use with DNS Plugins [\#29](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/29) ([yachub](https://github.com/yachub)) + ## [2.1.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/2.1.0) (2023-03-06) [Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/2.0.0...2.1.0) diff --git a/Gemfile.lock b/Gemfile.lock index aea3199..4398659 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (2.1.0) + vmpooler-provider-vsphere (3.0.0) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 3.0) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index 6be7f16..e33f914 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '2.1.0' + VERSION = '3.0.0' end From 473381b0a02cb89ef688761738a27292cab40cdd Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Wed, 19 Apr 2023 16:57:48 -0400 Subject: [PATCH 42/70] Bump jruby to 9.4.2.0 --- .github/workflows/release.yml | 2 +- .github/workflows/testing.yml | 4 ++-- install-gemfile-lock | 2 +- release-prep | 2 +- update-gemfile-lock | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0761b75..29be3d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,7 +73,7 @@ jobs: - name: Install Ruby jruby-9.3.6.0 uses: ruby/setup-ruby@v1 with: - ruby-version: 'jruby-9.4.1.0' + ruby-version: 'jruby-9.4.2.0' - name: Build gem run: gem build *.gemspec diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index eac55b3..54dab4b 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: ruby-version: - - 'jruby-9.4.1.0' + - 'jruby-9.4.2.0' steps: - uses: actions/checkout@v3 - name: Set up Ruby @@ -34,7 +34,7 @@ jobs: strategy: matrix: ruby-version: - - 'jruby-9.4.1.0' + - 'jruby-9.4.2.0' steps: - uses: actions/checkout@v3 - name: Set up Ruby diff --git a/install-gemfile-lock b/install-gemfile-lock index 07cbd49..94a5e5e 100755 --- a/install-gemfile-lock +++ b/install-gemfile-lock @@ -3,5 +3,5 @@ # The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment docker run -it --rm \ -v $(pwd):/app \ - jruby:9.4.1.0-jdk11 \ + jruby:9.4.2.0-jdk11 \ /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends make git netbase && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' diff --git a/release-prep b/release-prep index e3c2b0d..072115e 100755 --- a/release-prep +++ b/release-prep @@ -5,7 +5,7 @@ # Update Gemfile.lock docker run -it --rm \ -v $(pwd):/app \ - jruby:9.4.1.0-jdk11 \ + jruby:9.4.2.0-jdk11 \ /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends git make netbase && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' # Update Changelog diff --git a/update-gemfile-lock b/update-gemfile-lock index 46c4873..68ea73f 100755 --- a/update-gemfile-lock +++ b/update-gemfile-lock @@ -3,5 +3,5 @@ # The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment docker run -it --rm \ -v $(pwd):/app \ - jruby:9.4.1.0-jdk11 \ + jruby:9.4.2.0-jdk11 \ /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends git make netbase && cd /app && gem install bundler && bundle install --jobs 3 && bundle update; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' From 2f0e817b7cd1e0c8a591218afa54252689eee230 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Thu, 20 Apr 2023 08:51:12 -0400 Subject: [PATCH 43/70] Migrate issue management to Jira --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b269094..0529e18 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ - [Usage](#usage) - [Custom VM Config Attribute](#custom-vm-config-attribute) - [Update the Gemfile Lock](#update-the-gemfile-lock) + - [Submitting Issues](#submitting-issues) - [Releasing](#releasing) - [License](#license) @@ -30,6 +31,10 @@ To update the `Gemfile.lock` run `./update-gemfile-lock`. Verify, and update if needed, that the docker tag in the script and GitHub action workflows matches what is used in the [vmpooler-deployment Dockerfile](https://github.com/puppetlabs/vmpooler-deployment/blob/main/docker/Dockerfile). +## Submitting Issues + +Please file any issues or requests in Jira at where project development is tracked across all VMPooler related components. + ## Releasing Follow these steps to publish a new GitHub release, and build and push the gem to . From a6633d81d7c5e25f7f345d45dbdc7ae0004aff3b Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 1 May 2023 08:34:10 -0400 Subject: [PATCH 44/70] 3.1.0 release prep --- CHANGELOG.md | 9 +++++++++ Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 258cd22..0f47557 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [3.1.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.1.0) (2023-05-01) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.0.0...3.1.0) + +**Merged pull requests:** + +- Migrate issue management to Jira [\#34](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/34) ([yachub](https://github.com/yachub)) +- Bump jruby to 9.4.2.0 [\#33](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/33) ([yachub](https://github.com/yachub)) + ## [3.0.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.0.0) (2023-04-19) [Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/2.1.0...3.0.0) diff --git a/Gemfile.lock b/Gemfile.lock index 4398659..83c9c7e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (3.0.0) + vmpooler-provider-vsphere (3.1.0) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 3.0) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index e33f914..e31090c 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '3.0.0' + VERSION = '3.1.0' end From 9bb978e7028c85844d719e0726ad1e982f284e0b Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 1 May 2023 08:34:18 -0400 Subject: [PATCH 45/70] Comment changelog validation until jira support is added --- .github/workflows/release.yml | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29be3d7..39a358f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,26 +29,26 @@ jobs: echo "version=$version" >> $GITHUB_OUTPUT echo "Found version $version from lib/vmpooler-provider-vsphere/version.rb" - - name: Generate Changelog - uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 - with: - args: >- - --future-release ${{ steps.nv.outputs.version }} - env: - CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # - name: Generate Changelog + # uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 + # with: + # args: >- + # --future-release ${{ steps.nv.outputs.version }} + # env: + # CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Validate Changelog - run : | - set -e - if [[ -n $(git status --porcelain) ]]; then - echo "Here is the current git status:" - git status - echo - echo "The following changes were detected:" - git --no-pager diff - echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running `./update-changelog`" - exit 1 - fi + # - name: Validate Changelog + # run : | + # set -e + # if [[ -n $(git status --porcelain) ]]; then + # echo "Here is the current git status:" + # git status + # echo + # echo "The following changes were detected:" + # git --no-pager diff + # echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running `./update-changelog`" + # exit 1 + # fi - name: Generate Release Notes uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 From 3f34008795ce38c3c8e3ebaeca7b9115fffd08f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 21:58:18 +0000 Subject: [PATCH 46/70] Bump rack-test from 2.0.2 to 2.1.0 Bumps [rack-test](https://github.com/rack/rack-test) from 2.0.2 to 2.1.0. - [Release notes](https://github.com/rack/rack-test/releases) - [Changelog](https://github.com/rack/rack-test/blob/main/History.md) - [Commits](https://github.com/rack/rack-test/compare/v2.0.2...v2.1.0) --- updated-dependencies: - dependency-name: rack-test dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 83c9c7e..8114160 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,6 +25,7 @@ GEM ffi (1.15.5-java) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) + json (2.6.3) json (2.6.3-java) method_source (1.0.0) mock_redis (0.36.0) @@ -32,9 +33,12 @@ GEM mustermann (2.0.2) ruby2_keywords (~> 0.0.1) net-ldap (0.17.1) + nio4r (2.5.8) nio4r (2.5.8-java) nokogiri (1.14.2-java) racc (~> 1.4) + nokogiri (1.14.2-x86_64-linux) + racc (~> 1.4) opentelemetry-api (1.1.0) opentelemetry-common (0.19.6) opentelemetry-api (~> 1.0) @@ -78,17 +82,23 @@ GEM ast (~> 2.4.1) pickup (0.0.11) prometheus-client (2.1.0) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) pry (0.14.2-java) coderay (~> 1.1) method_source (~> 1.0) spoon (~> 0.0) + puma (5.6.5) + nio4r (~> 2.0) puma (5.6.5-java) nio4r (~> 2.0) + racc (1.6.2) racc (1.6.2-java) - rack (2.2.6.2) + rack (2.2.7) rack-protection (2.2.4) rack - rack-test (2.0.2) + rack-test (2.1.0) rack (>= 1.3) rainbow (3.1.1) rake (13.0.6) @@ -173,6 +183,7 @@ GEM PLATFORMS universal-java-11 + x86_64-linux DEPENDENCIES climate_control (>= 0.2.0) From 21417260daed04e788120728f6e3cca390185323 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 18:37:07 +0000 Subject: [PATCH 47/70] Bump vmpooler from 3.0.0 to 3.1.0 Bumps [vmpooler](https://github.com/puppetlabs/vmpooler) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/puppetlabs/vmpooler/releases) - [Changelog](https://github.com/puppetlabs/vmpooler/blob/main/CHANGELOG.md) - [Commits](https://github.com/puppetlabs/vmpooler/compare/3.0.0...3.1.0) --- updated-dependencies: - dependency-name: vmpooler dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8114160..70516ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,11 +30,11 @@ GEM method_source (1.0.0) mock_redis (0.36.0) ruby2_keywords - mustermann (2.0.2) + mustermann (3.0.0) ruby2_keywords (~> 0.0.1) - net-ldap (0.17.1) - nio4r (2.5.8) - nio4r (2.5.8-java) + net-ldap (0.18.0) + nio4r (2.5.9) + nio4r (2.5.9-java) nokogiri (1.14.2-java) racc (~> 1.4) nokogiri (1.14.2-x86_64-linux) @@ -42,10 +42,11 @@ GEM opentelemetry-api (1.1.0) opentelemetry-common (0.19.6) opentelemetry-api (~> 1.0) - opentelemetry-exporter-jaeger (0.20.1) - opentelemetry-api (~> 1.0) - opentelemetry-common (~> 0.19.2) - opentelemetry-sdk (~> 1.0) + opentelemetry-exporter-jaeger (0.22.0) + opentelemetry-api (~> 1.1) + opentelemetry-common (~> 0.19.6) + opentelemetry-sdk (~> 1.2) + opentelemetry-semantic_conventions thrift opentelemetry-instrumentation-base (0.19.0) opentelemetry-api (~> 1.0) @@ -66,9 +67,9 @@ GEM opentelemetry-instrumentation-base (~> 0.19.0) opentelemetry-registry (0.2.0) opentelemetry-api (~> 1.1) - opentelemetry-resource_detectors (0.19.1) + opentelemetry-resource_detectors (0.23.0) google-cloud-env - opentelemetry-sdk + opentelemetry-sdk (~> 1.0) opentelemetry-sdk (1.2.0) opentelemetry-api (~> 1.1) opentelemetry-common (~> 0.19.3) @@ -81,7 +82,7 @@ GEM parser (3.2.1.0) ast (~> 2.4.1) pickup (0.0.11) - prometheus-client (2.1.0) + prometheus-client (4.1.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -89,14 +90,14 @@ GEM coderay (~> 1.1) method_source (~> 1.0) spoon (~> 0.0) - puma (5.6.5) + puma (6.2.2) nio4r (~> 2.0) - puma (5.6.5-java) + puma (6.2.2-java) nio4r (~> 2.0) racc (1.6.2) racc (1.6.2-java) rack (2.2.7) - rack-protection (2.2.4) + rack-protection (3.0.6) rack rack-test (2.1.0) rack (>= 1.3) @@ -142,10 +143,10 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) - sinatra (2.2.4) - mustermann (~> 2.0) - rack (~> 2.2) - rack-protection (= 2.2.4) + sinatra (3.0.6) + mustermann (~> 3.0) + rack (~> 2.2, >= 2.2.4) + rack-protection (= 3.0.6) tilt (~> 2.0) spicy-proton (2.1.15) bindata (~> 2.3) @@ -156,25 +157,25 @@ GEM thrift (0.18.1) tilt (2.1.0) unicode-display_width (2.4.2) - vmpooler (3.0.0) + vmpooler (3.1.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) deep_merge (~> 1.2) net-ldap (~> 0.16) - opentelemetry-exporter-jaeger (= 0.20.1) + opentelemetry-exporter-jaeger (= 0.22.0) opentelemetry-instrumentation-concurrent_ruby (= 0.19.2) opentelemetry-instrumentation-http_client (= 0.19.4) opentelemetry-instrumentation-redis (= 0.21.3) opentelemetry-instrumentation-sinatra (= 0.19.3) - opentelemetry-resource_detectors (= 0.19.1) + opentelemetry-resource_detectors (= 0.23.0) opentelemetry-sdk (~> 1.0, >= 1.0.2) pickup (~> 0.0.11) - prometheus-client (~> 2.0) - puma (~> 5.0, >= 5.0.4) - rack (~> 2.2) + prometheus-client (>= 2, < 5) + puma (>= 5.0.4, < 7) + rack (>= 2.2, < 4.0) rake (~> 13.0) redis (~> 4.1) - sinatra (~> 2.0) + sinatra (>= 2, < 4) spicy-proton (~> 2.1) statsd-ruby (~> 1.4) yarjuf (2.0.0) From 922e99401782e421bfd903ada3cd8e2ad11ed709 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 21:58:13 +0000 Subject: [PATCH 48/70] Bump thor from 1.2.1 to 1.2.2 Bumps [thor](https://github.com/rails/thor) from 1.2.1 to 1.2.2. - [Release notes](https://github.com/rails/thor/releases) - [Commits](https://github.com/rails/thor/compare/v1.2.1...v1.2.2) --- updated-dependencies: - dependency-name: thor dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8114160..085d986 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -152,7 +152,7 @@ GEM spoon (0.0.6) ffi statsd-ruby (1.5.0) - thor (1.2.1) + thor (1.2.2) thrift (0.18.1) tilt (2.1.0) unicode-display_width (2.4.2) From 9e31ed815f728fb17d4a7e4892c95b1485446c4d Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Thu, 20 Apr 2023 08:51:12 -0400 Subject: [PATCH 49/70] Revert "Migrate issue management to Jira" This reverts commit 2f0e817b7cd1e0c8a591218afa54252689eee230. --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 0529e18..b269094 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ - [Usage](#usage) - [Custom VM Config Attribute](#custom-vm-config-attribute) - [Update the Gemfile Lock](#update-the-gemfile-lock) - - [Submitting Issues](#submitting-issues) - [Releasing](#releasing) - [License](#license) @@ -31,10 +30,6 @@ To update the `Gemfile.lock` run `./update-gemfile-lock`. Verify, and update if needed, that the docker tag in the script and GitHub action workflows matches what is used in the [vmpooler-deployment Dockerfile](https://github.com/puppetlabs/vmpooler-deployment/blob/main/docker/Dockerfile). -## Submitting Issues - -Please file any issues or requests in Jira at where project development is tracked across all VMPooler related components. - ## Releasing Follow these steps to publish a new GitHub release, and build and push the gem to . From 1e4719e670c9532ea35140f820baffcc12a69d80 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 1 May 2023 08:34:18 -0400 Subject: [PATCH 50/70] Revert "Comment changelog validation until jira support is added" This reverts commit 9bb978e7028c85844d719e0726ad1e982f284e0b. --- .github/workflows/release.yml | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 39a358f..29be3d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,26 +29,26 @@ jobs: echo "version=$version" >> $GITHUB_OUTPUT echo "Found version $version from lib/vmpooler-provider-vsphere/version.rb" - # - name: Generate Changelog - # uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 - # with: - # args: >- - # --future-release ${{ steps.nv.outputs.version }} - # env: - # CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Generate Changelog + uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 + with: + args: >- + --future-release ${{ steps.nv.outputs.version }} + env: + CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # - name: Validate Changelog - # run : | - # set -e - # if [[ -n $(git status --porcelain) ]]; then - # echo "Here is the current git status:" - # git status - # echo - # echo "The following changes were detected:" - # git --no-pager diff - # echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running `./update-changelog`" - # exit 1 - # fi + - name: Validate Changelog + run : | + set -e + if [[ -n $(git status --porcelain) ]]; then + echo "Here is the current git status:" + git status + echo + echo "The following changes were detected:" + git --no-pager diff + echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running `./update-changelog`" + exit 1 + fi - name: Generate Release Notes uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 From bf46ca2642f39096cee68f134bd8c29fa09b2457 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Thu, 10 Aug 2023 07:20:37 -0400 Subject: [PATCH 51/70] Bump jruby to 9.4.3.0 and update lockfile --- .github/workflows/release.yml | 2 +- .github/workflows/testing.yml | 4 +- Gemfile.lock | 122 ++++++++++++++++++---------------- install-gemfile-lock | 2 +- release-prep | 2 +- update-gemfile-lock | 2 +- 6 files changed, 70 insertions(+), 64 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29be3d7..5d71f9f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,7 +73,7 @@ jobs: - name: Install Ruby jruby-9.3.6.0 uses: ruby/setup-ruby@v1 with: - ruby-version: 'jruby-9.4.2.0' + ruby-version: 'jruby-9.4.3.0' - name: Build gem run: gem build *.gemspec diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 54dab4b..105fc8e 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: ruby-version: - - 'jruby-9.4.2.0' + - 'jruby-9.4.3.0' steps: - uses: actions/checkout@v3 - name: Set up Ruby @@ -34,7 +34,7 @@ jobs: strategy: matrix: ruby-version: - - 'jruby-9.4.2.0' + - 'jruby-9.4.3.0' steps: - uses: actions/checkout@v3 - name: Set up Ruby diff --git a/Gemfile.lock b/Gemfile.lock index d20929b..eecb540 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,11 +14,11 @@ GEM climate_control (1.2.0) coderay (1.1.3) concurrent-ruby (1.2.2) - connection_pool (2.4.0) + connection_pool (2.4.1) deep_merge (1.2.2) diff-lcs (1.5.0) docile (1.4.0) - faraday (2.7.4) + faraday (2.7.10) faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) faraday-net_http (3.0.2) @@ -28,61 +28,67 @@ GEM json (2.6.3) json (2.6.3-java) method_source (1.0.0) - mock_redis (0.36.0) - ruby2_keywords + mock_redis (0.37.0) mustermann (3.0.0) ruby2_keywords (~> 0.0.1) net-ldap (0.18.0) nio4r (2.5.9) nio4r (2.5.9-java) - nokogiri (1.14.2-java) + nokogiri (1.15.3-java) racc (~> 1.4) - nokogiri (1.14.2-x86_64-linux) + nokogiri (1.15.3-x86_64-linux) racc (~> 1.4) - opentelemetry-api (1.1.0) - opentelemetry-common (0.19.6) + opentelemetry-api (1.2.1) + opentelemetry-common (0.20.0) opentelemetry-api (~> 1.0) - opentelemetry-exporter-jaeger (0.22.0) + opentelemetry-exporter-jaeger (0.23.0) opentelemetry-api (~> 1.1) - opentelemetry-common (~> 0.19.6) + opentelemetry-common (~> 0.20) opentelemetry-sdk (~> 1.2) opentelemetry-semantic_conventions thrift - opentelemetry-instrumentation-base (0.19.0) + opentelemetry-instrumentation-base (0.22.2) opentelemetry-api (~> 1.0) - opentelemetry-instrumentation-concurrent_ruby (0.19.2) + opentelemetry-registry (~> 0.1) + opentelemetry-instrumentation-concurrent_ruby (0.21.1) opentelemetry-api (~> 1.0) - opentelemetry-instrumentation-base (~> 0.19.0) - opentelemetry-instrumentation-http_client (0.19.4) + opentelemetry-instrumentation-base (~> 0.22.1) + opentelemetry-instrumentation-http_client (0.22.2) opentelemetry-api (~> 1.0) - opentelemetry-common (~> 0.19.3) - opentelemetry-instrumentation-base (~> 0.19.0) - opentelemetry-instrumentation-redis (0.21.3) + opentelemetry-common (~> 0.20.0) + opentelemetry-instrumentation-base (~> 0.22.1) + opentelemetry-instrumentation-rack (0.23.4) opentelemetry-api (~> 1.0) - opentelemetry-common (~> 0.19.3) - opentelemetry-instrumentation-base (~> 0.19.0) - opentelemetry-instrumentation-sinatra (0.19.3) + opentelemetry-common (~> 0.20.0) + opentelemetry-instrumentation-base (~> 0.22.1) + opentelemetry-instrumentation-redis (0.25.3) opentelemetry-api (~> 1.0) - opentelemetry-common (~> 0.19.3) - opentelemetry-instrumentation-base (~> 0.19.0) - opentelemetry-registry (0.2.0) + opentelemetry-common (~> 0.20.0) + opentelemetry-instrumentation-base (~> 0.22.1) + opentelemetry-instrumentation-sinatra (0.23.2) + opentelemetry-api (~> 1.0) + opentelemetry-common (~> 0.20.0) + opentelemetry-instrumentation-base (~> 0.22.1) + opentelemetry-instrumentation-rack (~> 0.21) + opentelemetry-registry (0.3.0) opentelemetry-api (~> 1.1) - opentelemetry-resource_detectors (0.23.0) + opentelemetry-resource_detectors (0.24.1) google-cloud-env opentelemetry-sdk (~> 1.0) - opentelemetry-sdk (1.2.0) + opentelemetry-sdk (1.3.0) opentelemetry-api (~> 1.1) - opentelemetry-common (~> 0.19.3) + opentelemetry-common (~> 0.20) opentelemetry-registry (~> 0.2) opentelemetry-semantic_conventions - opentelemetry-semantic_conventions (1.8.0) + opentelemetry-semantic_conventions (1.10.0) opentelemetry-api (~> 1.0) - optimist (3.0.1) - parallel (1.22.1) - parser (3.2.1.0) + optimist (3.1.0) + parallel (1.23.0) + parser (3.2.2.3) ast (~> 2.4.1) + racc pickup (0.0.11) - prometheus-client (4.1.0) + prometheus-client (4.2.1) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -90,40 +96,40 @@ GEM coderay (~> 1.1) method_source (~> 1.0) spoon (~> 0.0) - puma (6.2.2) + puma (6.3.0) nio4r (~> 2.0) - puma (6.2.2-java) + puma (6.3.0-java) nio4r (~> 2.0) - racc (1.6.2) - racc (1.6.2-java) - rack (2.2.7) - rack-protection (3.0.6) - rack + racc (1.7.1) + racc (1.7.1-java) + rack (2.2.8) + rack-protection (3.1.0) + rack (~> 2.2, >= 2.2.4) rack-test (2.1.0) rack (>= 1.3) rainbow (3.1.1) rake (13.0.6) - rbvmomi2 (3.6.0) + rbvmomi2 (3.6.1) builder (~> 3.2) json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) redis (4.8.1) - regexp_parser (2.7.0) - rexml (3.2.5) + regexp_parser (2.8.1) + rexml (3.2.6) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) rspec-mocks (~> 3.12.0) - rspec-core (3.12.1) + rspec-core (3.12.2) rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rspec-expectations (3.12.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-mocks (3.12.3) + rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-support (3.12.0) + rspec-support (3.12.1) rubocop (1.28.2) parallel (~> 1.10) parser (>= 3.1.0.0) @@ -133,9 +139,9 @@ GEM rubocop-ast (>= 1.17.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.27.0) + rubocop-ast (1.29.0) parser (>= 3.2.1.0) - ruby-progressbar (1.12.0) + ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) simplecov (0.22.0) docile (~> 1.1) @@ -143,10 +149,10 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) - sinatra (3.0.6) + sinatra (3.1.0) mustermann (~> 3.0) rack (~> 2.2, >= 2.2.4) - rack-protection (= 3.0.6) + rack-protection (= 3.1.0) tilt (~> 2.0) spicy-proton (2.1.15) bindata (~> 2.3) @@ -155,20 +161,20 @@ GEM statsd-ruby (1.5.0) thor (1.2.2) thrift (0.18.1) - tilt (2.1.0) + tilt (2.2.0) unicode-display_width (2.4.2) - vmpooler (3.1.0) + vmpooler (3.2.0) concurrent-ruby (~> 1.1) connection_pool (~> 2.2) deep_merge (~> 1.2) net-ldap (~> 0.16) - opentelemetry-exporter-jaeger (= 0.22.0) - opentelemetry-instrumentation-concurrent_ruby (= 0.19.2) - opentelemetry-instrumentation-http_client (= 0.19.4) - opentelemetry-instrumentation-redis (= 0.21.3) - opentelemetry-instrumentation-sinatra (= 0.19.3) - opentelemetry-resource_detectors (= 0.23.0) - opentelemetry-sdk (~> 1.0, >= 1.0.2) + opentelemetry-exporter-jaeger (= 0.23.0) + opentelemetry-instrumentation-concurrent_ruby (= 0.21.1) + opentelemetry-instrumentation-http_client (= 0.22.2) + opentelemetry-instrumentation-redis (= 0.25.3) + opentelemetry-instrumentation-sinatra (= 0.23.2) + opentelemetry-resource_detectors (= 0.24.1) + opentelemetry-sdk (~> 1.3, >= 1.3.0) pickup (~> 0.0.11) prometheus-client (>= 2, < 5) puma (>= 5.0.4, < 7) diff --git a/install-gemfile-lock b/install-gemfile-lock index 94a5e5e..a479b0d 100755 --- a/install-gemfile-lock +++ b/install-gemfile-lock @@ -3,5 +3,5 @@ # The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment docker run -it --rm \ -v $(pwd):/app \ - jruby:9.4.2.0-jdk11 \ + jruby:9.4.3.0-jdk11 \ /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends make git netbase && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' diff --git a/release-prep b/release-prep index 072115e..ad040cc 100755 --- a/release-prep +++ b/release-prep @@ -5,7 +5,7 @@ # Update Gemfile.lock docker run -it --rm \ -v $(pwd):/app \ - jruby:9.4.2.0-jdk11 \ + jruby:9.4.3.0-jdk11 \ /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends git make netbase && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' # Update Changelog diff --git a/update-gemfile-lock b/update-gemfile-lock index 68ea73f..31986cc 100755 --- a/update-gemfile-lock +++ b/update-gemfile-lock @@ -3,5 +3,5 @@ # The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment docker run -it --rm \ -v $(pwd):/app \ - jruby:9.4.2.0-jdk11 \ + jruby:9.4.3.0-jdk11 \ /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends git make netbase && cd /app && gem install bundler && bundle install --jobs 3 && bundle update; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' From 677226e83b9d487a4ba5d93596b375c40bb56a1d Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Thu, 10 Aug 2023 12:22:00 -0400 Subject: [PATCH 52/70] 3.2.0 release prep --- CHANGELOG.md | 14 ++++++++++++++ Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f47557..e1df778 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [3.2.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.2.0) (2023-08-10) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.1.0...3.2.0) + +**Implemented enhancements:** + +- Bump jruby to 9.4.3.0 and update lockfile [\#40](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/40) ([yachub](https://github.com/yachub)) + +**Merged pull requests:** + +- Bump thor from 1.2.1 to 1.2.2 [\#38](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/38) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Bump vmpooler from 3.0.0 to 3.1.0 [\#37](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/37) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Bump rack-test from 2.0.2 to 2.1.0 [\#36](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/36) ([dependabot[bot]](https://github.com/apps/dependabot)) + ## [3.1.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.1.0) (2023-05-01) [Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.0.0...3.1.0) diff --git a/Gemfile.lock b/Gemfile.lock index eecb540..77488c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (3.1.0) + vmpooler-provider-vsphere (3.2.0) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 3.0) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index e31090c..9dbbdd3 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '3.1.0' + VERSION = '3.2.0' end From 5ca0555a52bd330df6bcc6eeae2fd3e8ef0a14af Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Thu, 17 Aug 2023 13:18:33 -0700 Subject: [PATCH 53/70] (POD-10) Log reason for failed VM checks. --- lib/vmpooler/providers/vsphere.rb | 7 ++++--- spec/unit/providers/vsphere_spec.rb | 12 +++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index 99271bc..48eabd3 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -555,14 +555,15 @@ module Vmpooler true end - def vm_ready?(pool_name, vm_name) + def vm_ready?(pool_name, vm_name, redis) begin domain = domain(pool_name) open_socket(vm_name, domain) - rescue StandardError => _e + rescue StandardError => e + redis.hset("vmpooler__vm__#{vm_name}", 'open_socket_error', e.to_s) return false end - + redis.hdel("vmpooler__vm__#{vm_name}", 'open_socket_error') true end diff --git a/spec/unit/providers/vsphere_spec.rb b/spec/unit/providers/vsphere_spec.rb index 2dbc22e..0dc2092 100644 --- a/spec/unit/providers/vsphere_spec.rb +++ b/spec/unit/providers/vsphere_spec.rb @@ -1090,7 +1090,9 @@ EOT end it 'should return true' do - expect(subject.vm_ready?(poolname, vmname)).to be true + redis_connection_pool.with do |redis| + expect(subject.vm_ready?(poolname, vmname, redis)).to be true + end end end @@ -1103,7 +1105,9 @@ EOT it 'should return true' do allow(subject).to receive(:domain).and_return('vmpooler.example.com') - expect(subject.vm_ready?('missing_pool',vmname)).to be true + redis_connection_pool.with do |redis| + expect(subject.vm_ready?('missing_pool', vmname, redis)).to be true + end end end @@ -1115,7 +1119,9 @@ EOT end it 'should return false' do - expect(subject.vm_ready?(poolname,vmname)).to be false + redis_connection_pool.with do |redis| + expect(subject.vm_ready?(poolname, vmname, redis)).to be false + end end end end From 506e874dad08214f25b2b4073e8bcb4730793dec Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Fri, 18 Aug 2023 08:41:54 -0700 Subject: [PATCH 54/70] (maint) Release prep for version 3.3.0 --- CHANGELOG.md | 8 ++++++++ Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1df778..9b9794b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [3.3.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.3.0) (2023-08-18) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.2.0...3.3.0) + +**Implemented enhancements:** + +- \(POD-10\) Log reason for failed VM checks. [\#42](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/42) ([isaac-hammes](https://github.com/isaac-hammes)) + ## [3.2.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.2.0) (2023-08-10) [Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.1.0...3.2.0) diff --git a/Gemfile.lock b/Gemfile.lock index 77488c7..2d7e14b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (3.2.0) + vmpooler-provider-vsphere (3.3.0) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 3.0) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index 9dbbdd3..e954ce9 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '3.2.0' + VERSION = '3.3.0' end From f02e0eb19a881a634b5bf8c27c35799039cd69b4 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 21 Aug 2023 21:20:36 -0400 Subject: [PATCH 55/70] Fix getting IP by not returning a bad address --- lib/vmpooler/providers/vsphere.rb | 2 ++ spec/rbvmomi_helper.rb | 2 +- spec/unit/providers/vsphere_spec.rb | 32 ++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index 48eabd3..fe6749e 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -603,9 +603,11 @@ module Vmpooler ip_loop_delay = 1 ip_loop_count = 1 ip = nil + invalid_addresses = /(0|169)\.(0|254)\.\d+\.\d+/ while ip.nil? sleep(ip_loop_delay) ip = vm_object.guest_ip + ip = nil if !ip.nil? && ip.match?(invalid_addresses) unless ip_maxloop == 0 break if ip_loop_count >= ip_maxloop diff --git a/spec/rbvmomi_helper.rb b/spec/rbvmomi_helper.rb index ace62b6..c045a3f 100644 --- a/spec/rbvmomi_helper.rb +++ b/spec/rbvmomi_helper.rb @@ -818,7 +818,7 @@ end def mock_RbVmomi_VIM_VirtualMachine(options = {}) options[:snapshot_tree] = nil if options[:snapshot_tree].nil? options[:name] = 'VM' + rand(65536).to_s if options[:name].nil? - options[:ip] = '169.254.255.255' if options[:ip].nil? + options[:ip] = '192.168.0.2' if options[:ip].nil? options[:path] = [] if options[:path].nil? mock = MockVirtualMachine.new() diff --git a/spec/unit/providers/vsphere_spec.rb b/spec/unit/providers/vsphere_spec.rb index 0dc2092..893b545 100644 --- a/spec/unit/providers/vsphere_spec.rb +++ b/spec/unit/providers/vsphere_spec.rb @@ -580,11 +580,41 @@ EOT end end + context 'when VM exists but contains a self assigned ip' do + let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ + :name => vmname, + :ip => '169.254.255.255', + }) + } + + it 'should return nil ip' do + allow(subject).to receive(:sleep) + result = subject.get_vm(poolname,vmname) + + expect(result['ip']).to eq(nil) + end + end + + context 'when VM exists but contains an invalid ip' do + let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ + :name => vmname, + :ip => '0.0.0.0', + }) + } + + it 'should return nil for ip' do + allow(subject).to receive(:sleep) + result = subject.get_vm(poolname,vmname) + + expect(result['ip']).to eq(nil) + end + end + context 'when VM exists and contains all information' do let(:vm_hostname) { "#{vmname}.demo.local" } let(:boot_time) { Time.now } let(:power_state) { 'MockPowerState' } - let(:ip) { '169.254.255.255' } + let(:ip) { '192.168.0.2' } let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname, From 7316431f783c2f963cb053872064895faf6664c0 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Mon, 21 Aug 2023 21:44:31 -0400 Subject: [PATCH 56/70] Increase timeout for getting an IP --- lib/vmpooler/providers/vsphere.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index fe6749e..358727f 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -599,7 +599,7 @@ module Vmpooler boottime = vm_object.runtime.bootTime if vm_object.runtime&.bootTime powerstate = vm_object.runtime.powerState if vm_object.runtime&.powerState - ip_maxloop = 60 + ip_maxloop = 120 ip_loop_delay = 1 ip_loop_count = 1 ip = nil From f0c7d377d18bd70939bc97974ea810a191fdc852 Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Tue, 22 Aug 2023 08:23:04 -0400 Subject: [PATCH 57/70] 3.3.1 release prep --- CHANGELOG.md | 8 ++++++++ Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9794b..6ceb962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [3.3.1](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.3.1) (2023-08-22) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.3.0...3.3.1) + +**Fixed bugs:** + +- \(RE-15710\) Fix IP address that is returned and increase timeout [\#44](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/44) ([yachub](https://github.com/yachub)) + ## [3.3.0](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.3.0) (2023-08-18) [Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.2.0...3.3.0) diff --git a/Gemfile.lock b/Gemfile.lock index 2d7e14b..6451e9e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (3.3.0) + vmpooler-provider-vsphere (3.3.1) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 3.0) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index e954ce9..b3bbe34 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '3.3.0' + VERSION = '3.3.1' end From 978fc0031ac5998b9e071b94945daa22fd1116fd Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Wed, 23 Aug 2023 05:46:49 -0700 Subject: [PATCH 58/70] (maint) Increase timeout for cloned vms to obtain IPs. --- lib/vmpooler/providers/vsphere.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index 358727f..c5e3212 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -599,7 +599,7 @@ module Vmpooler boottime = vm_object.runtime.bootTime if vm_object.runtime&.bootTime powerstate = vm_object.runtime.powerState if vm_object.runtime&.powerState - ip_maxloop = 120 + ip_maxloop = 240 ip_loop_delay = 1 ip_loop_count = 1 ip = nil From 69629e1019a7d310d24e3e94e28387b525a65440 Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Wed, 23 Aug 2023 06:03:47 -0700 Subject: [PATCH 59/70] (maint) Release prep for version 3.3.2 --- CHANGELOG.md | 8 ++++++++ Gemfile.lock | 2 +- lib/vmpooler-provider-vsphere/version.rb | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ceb962..dcb5fe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [3.3.2](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.3.2) (2023-08-23) + +[Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.3.1...3.3.2) + +**Fixed bugs:** + +- \(maint\) Increase timeout for cloned vms to obtain IPs. [\#47](https://github.com/puppetlabs/vmpooler-provider-vsphere/pull/47) ([isaac-hammes](https://github.com/isaac-hammes)) + ## [3.3.1](https://github.com/puppetlabs/vmpooler-provider-vsphere/tree/3.3.1) (2023-08-22) [Full Changelog](https://github.com/puppetlabs/vmpooler-provider-vsphere/compare/3.3.0...3.3.1) diff --git a/Gemfile.lock b/Gemfile.lock index 6451e9e..44930ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - vmpooler-provider-vsphere (3.3.1) + vmpooler-provider-vsphere (3.3.2) rbvmomi2 (>= 3.1, < 4.0) vmpooler (~> 3.0) diff --git a/lib/vmpooler-provider-vsphere/version.rb b/lib/vmpooler-provider-vsphere/version.rb index b3bbe34..5952ba7 100644 --- a/lib/vmpooler-provider-vsphere/version.rb +++ b/lib/vmpooler-provider-vsphere/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VmpoolerProviderVsphere - VERSION = '3.3.1' + VERSION = '3.3.2' end From 81f5192ded0ac5b0498a55bca039b253d127ac98 Mon Sep 17 00:00:00 2001 From: isaac-hammes Date: Mon, 28 Aug 2023 07:39:11 -0700 Subject: [PATCH 60/70] (maint) Update Gemfile.lock and use block for transaction. --- Gemfile.lock | 21 ++++++++++++--------- lib/vmpooler/providers/vsphere.rb | 30 +++++++++++++++--------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 44930ce..220fd6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,11 +34,11 @@ GEM net-ldap (0.18.0) nio4r (2.5.9) nio4r (2.5.9-java) - nokogiri (1.15.3-java) + nokogiri (1.15.4-java) racc (~> 1.4) - nokogiri (1.15.3-x86_64-linux) + nokogiri (1.15.4-x86_64-linux) racc (~> 1.4) - opentelemetry-api (1.2.1) + opentelemetry-api (1.2.2) opentelemetry-common (0.20.0) opentelemetry-api (~> 1.0) opentelemetry-exporter-jaeger (0.23.0) @@ -96,9 +96,9 @@ GEM coderay (~> 1.1) method_source (~> 1.0) spoon (~> 0.0) - puma (6.3.0) + puma (6.3.1) nio4r (~> 2.0) - puma (6.3.0-java) + puma (6.3.1-java) nio4r (~> 2.0) racc (1.7.1) racc (1.7.1-java) @@ -114,7 +114,10 @@ GEM json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) - redis (4.8.1) + redis (5.0.7) + redis-client (>= 0.9.0) + redis-client (0.16.0) + connection_pool regexp_parser (2.8.1) rexml (3.2.6) rspec (3.12.0) @@ -163,9 +166,9 @@ GEM thrift (0.18.1) tilt (2.2.0) unicode-display_width (2.4.2) - vmpooler (3.2.0) + vmpooler (3.5.1) concurrent-ruby (~> 1.1) - connection_pool (~> 2.2) + connection_pool (~> 2.4) deep_merge (~> 1.2) net-ldap (~> 0.16) opentelemetry-exporter-jaeger (= 0.23.0) @@ -180,7 +183,7 @@ GEM puma (>= 5.0.4, < 7) rack (>= 2.2, < 4.0) rake (~> 13.0) - redis (~> 4.1) + redis (~> 5.0) sinatra (>= 2, < 4) spicy-proton (~> 2.1) statsd-ruby (~> 1.4) diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index c5e3212..a67ca7e 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -68,14 +68,14 @@ module Vmpooler try = 0 if try.nil? max_tries = 3 @redis.with_metrics do |redis| - redis.multi - redis.srem("vmpooler__completed__#{pool}", vm_name) - redis.hdel("vmpooler__active__#{pool}", vm_name) - redis.hset("vmpooler__vm__#{vm_name}", 'destroy', Time.now) + redis.multi do |transaction| + transaction.srem("vmpooler__completed__#{pool}", vm_name) + transaction.hdel("vmpooler__active__#{pool}", vm_name) + transaction.hset("vmpooler__vm__#{vm_name}", 'destroy', Time.now) - # Auto-expire metadata key - redis.expire("vmpooler__vm__#{vm_name}", (data_ttl * 60 * 60)) - redis.exec + # Auto-expire metadata key + transaction.expire("vmpooler__vm__#{vm_name}", (data_ttl * 60 * 60)) + end end start = Time.now @@ -1137,10 +1137,10 @@ module Vmpooler target_host_object = find_host_by_dnsname(connection, target_host_name) finish = migrate_vm_and_record_timing(pool_name, vm_name, vm_hash, target_host_object, target_host_name) @redis.with_metrics do |redis| - redis.multi - redis.hset("vmpooler__vm__#{vm_name}", 'host', target_host_name) - redis.hset("vmpooler__vm__#{vm_name}", 'migrated', true) - redis.exec + redis.multi do |transaction| + transaction.hset("vmpooler__vm__#{vm_name}", 'host', target_host_name) + transaction.hset("vmpooler__vm__#{vm_name}", 'migrated', true) + end end logger.log('s', "[>] [#{pool_name}] '#{vm_name}' migrated from #{vm_hash['host_name']} to #{target_host_name} in #{finish} seconds") ensure @@ -1158,10 +1158,10 @@ module Vmpooler metrics.increment("migrate_to.#{dest_host_name}") @redis.with_metrics do |redis| checkout_to_migration = format('%