diff options
author | Lamont Granquist <lamont@chef.io> | 2020-02-03 18:05:57 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-03 18:05:57 -0800 |
commit | 7a32aa5465446a4b63442a81b2ba6c8b77cd5097 (patch) | |
tree | 9ef6521ec82f26c397ea4dfcb8498b601fcc29bd | |
parent | 5f1d29e6c2f5ab15ea89ff3412b92052dd9bcb11 (diff) | |
parent | 492d4199535f8e0bfc64c970063033ae4541cf05 (diff) | |
download | chef-7a32aa5465446a4b63442a81b2ba6c8b77cd5097.tar.gz |
Merge pull request #9315 from chef/lcg/chef-sugar-virtualization
Add chef-sugar virtualization helpers
-rw-r--r-- | chef-utils/README.md | 19 | ||||
-rw-r--r-- | chef-utils/lib/chef-utils.rb | 4 | ||||
-rw-r--r-- | chef-utils/lib/chef-utils/dsl/virtualization.rb | 231 | ||||
-rw-r--r-- | chef-utils/spec/spec_helper.rb | 8 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/virtualization_spec.rb | 74 | ||||
-rw-r--r-- | lib/chef/node/attribute.rb | 4 | ||||
-rw-r--r-- | lib/chef/node/common_api.rb | 4 |
7 files changed, 338 insertions, 6 deletions
diff --git a/chef-utils/README.md b/chef-utils/README.md index 8116f84c5e..668739c9c1 100644 --- a/chef-utils/README.md +++ b/chef-utils/README.md @@ -133,6 +133,25 @@ Architecture Helpers allow you to determine the processor architecture of your n * `digital_ocean?` - if the node is running in digital ocean * `softlayer?` - if the node is running in softlayer +### Virtualization Helpers + +* `kvm?` - if the node is a kvm guest +* `kvm_host?` - if the node is a kvm host +* `lxc?` - if the node is an lxc guest +* `lxc_host?` - if the node is an lxc host +* `parallels?`- if the node is a parallels guest +* `parallels_host?`- if the node is a parallels host +* `vbox?` - if the node is a virtualbox guest +* `vbox_host?` - if the node is a virtualbox host +* `vmware?` - if the node is a vmware guest +* `vmware_host?` - if the node is a vmware host +* `openvz?` - if the node is an openvz guest +* `openvz_host?` - if the node is an openvz host +* `guest?` - if the node is detected as any kind of guest +* `hypervisor?` - if the node is detected as being any kind of hypervisor +* `physical?` - the node is not running as a guest (may be a hypervisor or may be plain metal) +* `vagrant?` - attempts to identify the node as a vagrant guest (this check may be error prone) + ### Train Helpers **EXPERIMENTAL**: APIs may have breaking changes any time without warning diff --git a/chef-utils/lib/chef-utils.rb b/chef-utils/lib/chef-utils.rb index d52c866de5..39c74d3f68 100644 --- a/chef-utils/lib/chef-utils.rb +++ b/chef-utils/lib/chef-utils.rb @@ -24,11 +24,12 @@ require_relative "chef-utils/dsl/platform" require_relative "chef-utils/dsl/platform_family" require_relative "chef-utils/dsl/service" require_relative "chef-utils/dsl/train_helpers" +require_relative "chef-utils/dsl/virtualization" require_relative "chef-utils/dsl/which" require_relative "chef-utils/dsl/windows" require_relative "chef-utils/mash" -# This is the Chef Infra Client DSL, not everytihng needs to go in here +# This is the Chef Infra Client DSL, not everything needs to go in here module ChefUtils include ChefUtils::DSL::Architecture include ChefUtils::DSL::Cloud @@ -38,6 +39,7 @@ module ChefUtils include ChefUtils::DSL::Platform include ChefUtils::DSL::PlatformFamily include ChefUtils::DSL::TrainHelpers + include ChefUtils::DSL::Virtualization include ChefUtils::DSL::Which include ChefUtils::DSL::Windows # ChefUtils::DSL::Service is deliberately excluded diff --git a/chef-utils/lib/chef-utils/dsl/virtualization.rb b/chef-utils/lib/chef-utils/dsl/virtualization.rb new file mode 100644 index 0000000000..df3a1ac3eb --- /dev/null +++ b/chef-utils/lib/chef-utils/dsl/virtualization.rb @@ -0,0 +1,231 @@ +# +# Copyright:: Copyright 2018-2020, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require_relative "../internal" + +module ChefUtils + module DSL + module Virtualization + include Internal + + # Determine if the current node is a KVM guest. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def kvm?(node = __getnode) + node.dig("virtualization", "system") == "kvm" && node.dig("virtualization", "role") == "guest" + end + + # Determine if the current node is a KVM host. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def kvm_host?(node = __getnode) + node.dig("virtualization", "system") == "kvm" && node.dig("virtualization", "role") == "host" + end + + # Determine if the current node is running in a linux container. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def lxc?(node = __getnode) + node.dig("virtualization", "system") == "lxc" && node.dig("virtualization", "role") == "guest" + end + + # Determine if the current node is a linux container host. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def lxc_host?(node = __getnode) + node.dig("virtualization", "system") == "lxc" && node.dig("virtualization", "role") == "host" + end + + # Determine if the current node is running under Parallels Desktop. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # true if the machine is currently running under Parallels Desktop, false + # otherwise + # + def parallels?(node = __getnode) + node.dig("virtualization", "system") == "parallels" && node.dig("virtualization", "role") == "guest" + end + + # Determine if the current node is a Parallels Desktop host. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # true if the machine is currently running under Parallels Desktop, false + # otherwise + # + def parallels_host?(node = __getnode) + node.dig("virtualization", "system") == "parallels" && node.dig("virtualization", "role") == "host" + end + + # Determine if the current node is a VirtualBox guest. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def vbox?(node = __getnode) + node.dig("virtualization", "system") == "vbox" && node.dig("virtualization", "role") == "guest" + end + + # Determine if the current node is a VirtualBox host. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def vbox_host?(node = __getnode) + node.dig("virtualization", "system") == "vbox" && node.dig("virtualization", "role") == "host" + end + + alias_method :virtualbox?, :vbox? + + # Determine if the current node is a VMWare guest. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def vmware?(node = __getnode) + node.dig("virtualization", "system") == "vmware" && node.dig("virtualization", "role") == "guest" + end + + # Determine if the current node is VMware host. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def vmware_host?(node = __getnode) + node.dig("virtualization", "system") == "vmware" && node.dig("virtualization", "role") == "host" + end + + # Determine if the current node is an openvz guest. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def openvz?(node = __getnode) + node.dig("virtualization", "system") == "openvz" && node.dig("virtualization", "role") == "guest" + end + + # Determine if the current node is an openvz host. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def openvz_host?(node = __getnode) + node.dig("virtualization", "system") == "openvz" && node.dig("virtualization", "role") == "host" + end + + # Determine if the current node is running under any virutalization environment + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def guest?(node = __getnode) + node.dig("virtualization", "role") == "guest" + end + + alias_method :virtual?, :guest? + + # Determine if the current node supports running guests under any virtualization environment + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def hypervisor?(node = __getnode) + node.dig("virtualization", "role") == "host" + end + + # Determine if the current node is NOT running under any virtualization environment (plain metal or hypervisor on metal) + # + # @param [Chef::Node] node + # + # @return [Boolean] + # + def physical?(node = __getnode) + !virtual?(node) + end + + # Determine if the current node is running as a vagrant guest. + # + # Note that this API is equivalent to just looking for the vagrant user or the + # vagrantup.com domain in the hostname, which is the best API we have. + # + # @param [Chef::Node] node + # + # @return [Boolean] + # true if the machine is currently running vagrant, false + # otherwise + # + def vagrant?(node = __getnode) + vagrant_key?(node) || vagrant_domain?(node) || vagrant_user?(node) + end + + private + + # Check if the +vagrant+ key exists on the +node+ object. This key is no + # longer populated by vagrant, but it is kept around for legacy purposes. + # + # @param (see vagrant?) + # @return (see vagrant?) + # + def vagrant_key?(node = __getnode) + node.key?("vagrant") + end + + # Check if "vagrantup.com" is included in the node's domain. + # + # @param (see vagrant?) + # @return (see vagrant?) + # + def vagrant_domain?(node = __getnode) + node.key?("domain") && !node["domain"].nil? && node["domain"].include?("vagrantup.com") + end + + # Check if the system contains a +vagrant+ user. + # + # @param (see vagrant?) + # @return (see vagrant?) + # + def vagrant_user?(node = __getnode) + !!(Etc.getpwnam("vagrant") rescue nil) + end + + extend self + end + end +end diff --git a/chef-utils/spec/spec_helper.rb b/chef-utils/spec/spec_helper.rb index 4d9b5518d1..a44377336e 100644 --- a/chef-utils/spec/spec_helper.rb +++ b/chef-utils/spec/spec_helper.rb @@ -10,17 +10,19 @@ HELPER_MODULES = [ ChefUtils::DSL::Platform, ChefUtils::DSL::PlatformFamily, ChefUtils::DSL::Service, + ChefUtils::DSL::Virtualization, ChefUtils::DSL::Which, ChefUtils::DSL::Windows, ].freeze ARCH_HELPERS = (ChefUtils::DSL::Architecture.methods - Module.methods).freeze +CLOUD_HELPERS = (ChefUtils::DSL::Cloud.methods - Module.methods).freeze +INTROSPECTION_HELPERS = (ChefUtils::DSL::Introspection.methods - Module.methods).freeze OS_HELPERS = (ChefUtils::DSL::OS.methods - Module.methods).freeze -PLATFORM_HELPERS = (ChefUtils::DSL::Platform.methods - Module.methods).freeze PLATFORM_FAMILY_HELPERS = (ChefUtils::DSL::PlatformFamily.methods - Module.methods).freeze -INTROSPECTION_HELPERS = (ChefUtils::DSL::Introspection.methods - Module.methods).freeze +PLATFORM_HELPERS = (ChefUtils::DSL::Platform.methods - Module.methods).freeze +VIRTUALIZATION_HELPERS = (ChefUtils::DSL::Virtualization.methods - Module.methods).freeze WINDOWS_HELPERS = (ChefUtils::DSL::Windows.methods - Module.methods).freeze -CLOUD_HELPERS = (ChefUtils::DSL::Cloud.methods - Module.methods).freeze # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| diff --git a/chef-utils/spec/unit/dsl/virtualization_spec.rb b/chef-utils/spec/unit/dsl/virtualization_spec.rb new file mode 100644 index 0000000000..84daa9e162 --- /dev/null +++ b/chef-utils/spec/unit/dsl/virtualization_spec.rb @@ -0,0 +1,74 @@ +# +# Copyright:: Copyright 2018-2020, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" +require "fauxhai" + +def virtualization_reports_true_for(*args, node:) + args.each do |method| + it "reports true for #{method}" do + expect(described_class.send(method, node)).to be true + end + end + (VIRTUALIZATION_HELPERS - args).each do |method| + it "reports false for #{method}" do + expect(described_class.send(method, node)).to be false + end + end +end + +RSpec.describe ChefUtils::DSL::Virtualization do + ( HELPER_MODULES - [ described_class ] ).each do |klass| + it "does not have methods that collide with #{klass}" do + expect((klass.methods - Module.methods) & VIRTUALIZATION_HELPERS).to be_empty + end + end + + VIRTUALIZATION_HELPERS.each do |helper| + it "has the #{helper} in the ChefUtils module" do + expect(ChefUtils).to respond_to(helper) + end + end + + context "on kvm" do + virtualization_reports_true_for(:guest?, :virtual?, :kvm?, node: { "virtualization" => { "system" => "kvm", "role" => "guest" } }) + virtualization_reports_true_for(:hypervisor?, :physical?, :kvm_host?, node: { "virtualization" => { "system" => "kvm", "role" => "host" } }) + end + context "on lxc" do + virtualization_reports_true_for(:guest?, :virtual?, :lxc?, node: { "virtualization" => { "system" => "lxc", "role" => "guest" } }) + virtualization_reports_true_for(:hypervisor?, :physical?, :lxc_host?, node: { "virtualization" => { "system" => "lxc", "role" => "host" } }) + end + context "on parallels" do + virtualization_reports_true_for(:guest?, :virtual?, :parallels?, node: { "virtualization" => { "system" => "parallels", "role" => "guest" } }) + virtualization_reports_true_for(:hypervisor?, :physical?, :parallels_host?, node: { "virtualization" => { "system" => "parallels", "role" => "host" } }) + end + context "on virtualbox" do + virtualization_reports_true_for(:guest?, :virtual?, :virtualbox?, :vbox?, node: { "virtualization" => { "system" => "vbox", "role" => "guest" } }) + virtualization_reports_true_for(:hypervisor?, :physical?, :vbox_host?, node: { "virtualization" => { "system" => "vbox", "role" => "host" } }) + end + context "on vmware" do + virtualization_reports_true_for(:guest?, :virtual?, :vmware?, node: { "virtualization" => { "system" => "vmware", "role" => "guest" } }) + virtualization_reports_true_for(:hypervisor?, :physical?, :vmware_host?, node: { "virtualization" => { "system" => "vmware", "role" => "host" } }) + end + context "on openvz" do + virtualization_reports_true_for(:guest?, :virtual?, :openvz?, node: { "virtualization" => { "system" => "openvz", "role" => "guest" } }) + virtualization_reports_true_for(:hypervisor?, :physical?, :openvz_host?, node: { "virtualization" => { "system" => "openvz", "role" => "host" } }) + end + context "on metal which is not a virt host" do + virtualization_reports_true_for(:physical?, node: {} ) + end +end diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index d872e53d9b..4ccf04af6a 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -1,7 +1,7 @@ #-- # Author:: Adam Jacob (<adam@chef.io>) # Author:: AJ Christensen (<aj@chef.io>) -# Copyright:: Copyright 2008-2019, Chef Software Inc. +# Copyright:: Copyright 2008-2020, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -453,6 +453,8 @@ class Chef merged_attributes.read(*path) end + alias :dig :read + def read!(*path) merged_attributes.read!(*path) end diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb index 388b2b5e23..829552d80c 100644 --- a/lib/chef/node/common_api.rb +++ b/lib/chef/node/common_api.rb @@ -1,5 +1,5 @@ #-- -# Copyright:: Copyright 2016, Chef Software, Inc. +# Copyright:: Copyright 2016-2020, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -98,6 +98,8 @@ class Chef nil end + alias :dig :read + # non-autovivifying reader that throws an exception if the attribute does not exist def read!(*path) raise Chef::Exceptions::NoSuchAttribute.new(path.join ".") unless exist?(*path) |