pax_global_header00006660000000000000000000000064140201571610014507gustar00rootroot0000000000000052 comment=23a173aa7eb6f14e1cc86c15a599c29cede24393 kitchen-salt-0.7.0/000077500000000000000000000000001402015716100141015ustar00rootroot00000000000000kitchen-salt-0.7.0/.drone.yml000066400000000000000000000030111402015716100160040ustar00rootroot00000000000000pipeline: build_centos: group: build image: gtmanfred/kitchen-salt:latest environment: - DOCKER_HOST=tcp://172.17.0.1:2375 commands: - bundle install --with=docker --without windows vagrant - bundle exec kitchen create centos -l warn -c 4 - bundle exec kitchen converge centos build_debian: group: build image: gtmanfred/kitchen-salt:latest environment: - DOCKER_HOST=tcp://172.17.0.1:2375 commands: - bundle install --with=docker --without windows vagrant - bundle exec kitchen create debian -l warn -c 4 - bundle exec kitchen converge debian build_ubuntu: group: build image: gtmanfred/kitchen-salt:latest environment: - DOCKER_HOST=tcp://172.17.0.1:2375 commands: - bundle install --with=docker --without windows vagrant - bundle exec kitchen create ubuntu -l warn -c 4 - bundle exec kitchen converge ubuntu verify: image: gtmanfred/kitchen-salt:latest environment: - DOCKER_HOST=tcp://172.17.0.1:2375 commands: - bundle install --with=docker --without windows vagrant - python3 -m pip install --user -r test-requirements.txt - bundle exec kitchen verify all clean: when: status: - failure - success image: gtmanfred/kitchen-salt:latest environment: - DOCKER_HOST=tcp://172.17.0.1:2375 commands: - bundle install --with=docker --without windows vagrant - bundle exec kitchen list - bundle exec kitchen destroy all kitchen-salt-0.7.0/.github/000077500000000000000000000000001402015716100154415ustar00rootroot00000000000000kitchen-salt-0.7.0/.github/workflows/000077500000000000000000000000001402015716100174765ustar00rootroot00000000000000kitchen-salt-0.7.0/.github/workflows/tests.yml000066400000000000000000000033071402015716100213660ustar00rootroot00000000000000name: Tests on: [push, pull_request] jobs: Tests: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: os: - centos - debian - ubuntu steps: - uses: actions/checkout@v2 - name: Set Python Cache Key run: echo "PY=$(python --version --version | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - uses: actions/cache@v2 with: path: | bundle virtualenv key: Tests-${{ matrix.os }}-${{ env.PY }}-${{ hashFiles('Gemfile', 'test-requirements.txt') }} - name: Install System Deps run: | sudo apt-get update sudo apt-get install -y curl git python3-pip ruby-dev ruby-bundler shellcheck virtualenv - name: Install Python Deps run: | if [ ! -e virtualenv/bin/activate ]; then /usr/bin/virtualenv -p /usr/bin/python3 virtualenv; fi . virtualenv/bin/activate pip3 install -U -r test-requirements.txt - name: Install Ruby Deps run: | /usr/bin/bundle config set --local path 'bundle' /usr/bin/bundle config set --local without 'vagrant windows' /usr/bin/bundle install - name: Install GPG Key run: | install -d -m 700 gpgkeys gpg --no-default-keyring --homedir gpgkeys --import < tests/gpgkeys/test.txt - name: Test run: | . virtualenv/bin/activate shellcheck lib/kitchen/provisioner/*.sh* shellcheck assets/*.sh* LANG=en_US.UTF-8 /usr/bin/bundle exec rake "integration:verify[${{ matrix.os }}]" /usr/bin/bundle exec kitchen list ${{ matrix.os }} - name: Cleanup run: | /usr/bin/bundle exec rake "integration:destroy[${{ matrix.os }}]" kitchen-salt-0.7.0/.gitignore000066400000000000000000000007611402015716100160750ustar00rootroot00000000000000# Because this is a gem, ignore Gemfile.lock: Gemfile.lock # And because this is Ruby, ignore the following # (source: https://github.com/github/gitignore/blob/master/Ruby.gitignore): *.gem *.rbc .bundle .config coverage InstalledFiles lib/bundler/man pkg rdoc test/tmp test/version_tmp tmp /gpgkeys .cache __pycache__ # YARD artifacts .yardoc _yardoc doc/ html/ # Other *.DS_Store .kitchen/ ### IntelliJ IDEA *.iml *.ipr *.iws .idea/ /out/ .idea_modules/ atlassian-ide-plugin.xml .vscode kitchen-salt-0.7.0/.kitchen.yml000066400000000000000000000140041402015716100163260ustar00rootroot00000000000000--- <% @vagrant = system('which vagrant 2>&1 >/dev/null') %> driver: name: docker hostname: kitchen-salt.ci.local use_sudo: false socket: <%= ENV['DOCKER_HOST'] || 'unix:///var/run/docker.sock' %> provision_command: - DEBIAN_FRONTEND=noninteractive apt-get install -y python3-pip python3-dev gcc git locales console-data gnupg - mkdir -p /run/sshd - echo en_US.UTF-8 UTF-8 >> /etc/locale.gen; locale-gen; update-locale 'LANG="en_US.UTF-8"' - echo export LANG=en_US.UTF-8 > ~kitchen/.bashrc - echo 'export PATH=$PATH:/usr/sbin:/sbin' >> ~kitchen/.bashrc provisioner: name: salt_solo salt_install: bootstrap salt_version: 3000.6 salt_bootstrap_url: https://bootstrap.saltproject.io salt_bootstrap_options: -X -x python3 -p git -p curl -p sudo stable 3000.6 pip_bin: pip3 formula: tests require_chef: false gpg_key: test@example.com gpg_home: gpgkeys cache_commands: - touch %{sandbox_path}/srv/salt/cache_commands_test init_environment: | sudo mkdir -p /tmp/kitchen/var/cache/salt/master salt_copy_filter: - .filter_hidden vendor_repo: - type: apt url: http://apt.mirantis.com/trusty key_url: http://apt.mirantis.com/public.gpg components: salt distribution: nightly - type: ppa name: neovim-ppa/unstable - type: spm url: https://spm.hubblestack.io/2016.7.1 name: hubblestack dependencies: - name: foo path: ./tests/formula-foo - name: nginx repo: apt package: salt-formula-nginx - name: linux repo: git source: https://github.com/salt-formulas/salt-formula-linux.git - name: git repo: git source: https://github.com/gtmanfred/salt-formula-git.git - name: postfix repo: git source: https://github.com/salt-formulas/salt-formula-postfix - name: hubblestack_nova repo: spm package: https://spm.hubblestack.io/nova/hubblestack_nova-2016.10.1-1.spm state_top: base: "*": - tests.sudo - git pillars: top.sls: base: "*": - git - gpgtest pillars_from_directories: - source: tests/pillars/ dest: srv/pillar/ grains: noservices: True platforms: - name: ubuntu-18.04 driver: run_command: '/usr/sbin/sshd -D -o UseDNS=no -o UsePAM=no -o PasswordAuthentication=yes -o PidFile=/tmp/sshd.pid' provisioner: salt_apt_repo: 'https://repo.saltproject.io/py3/ubuntu/18.04/amd64/archive' salt_apt_repo_key: 'https://repo.saltproject.io/py3/ubuntu/18.04/amd64/latest/SALTSTACK-GPG-KEY.pub' init_environment: | sudo mkdir -p /tmp/kitchen/var/cache/salt/master sudo apt-get update DEBIAN_FRONTEND=noninteractive sudo apt-get install -y software-properties-common salt_bootstrap_options: -X -x python3 -p git -p curl -p sudo -p software-properties-common stable 3000.6 - name: debian-10 provisioner: salt_apt_repo: 'https://repo.saltproject.io/py3/debian/10/amd64/archive' salt_apt_repo_key: 'https://repo.saltproject.io/py3/debian/10/amd64/latest/SALTSTACK-GPG-KEY.pub' - name: centos-8 driver_config: provision_command: - echo . /etc/profile >> ~kitchen/.bashrc - echo export LANG=en_US.UTF-8 >> ~kitchen/.bashrc - sed -i '/secure_path/d' /etc/sudoers - yum install -y epel-release - yum install -y python3-pip python3-devel gcc git gcc-c++ provisioner: salt_yum_repo: 'https://repo.saltproject.io/py3/redhat/8/x86_64/archive/%s' salt_yum_rpm_key: 'https://repo.saltproject.io/py3/redhat/8/x86_64/latest/SALTSTACK-GPG-KEY.pub' <% if @vagrant != false %> - name: windows-2012r2 driver: box: opentable/win-2012r2-datacenter-amd64-nocm communicator: winrm name: vagrant gui: true provisioner: # On Osx 'brew install unix2dos' or Centos 'yum install tofrodos' cache_commands: - touch %{sandbox_path}/srv/salt/cache_commands_test - find %{sandbox_path} -type f | xargs unix2dos init_environment: | Clear-Host $AddedLocation ="c:\salt" $Reg = "Registry::HKLM\System\CurrentControlSet\Control\Session Manager\Environment" $OldPath = (Get-ItemProperty -Path "$Reg" -Name PATH).Path $NewPath= $OldPath + ’;’ + $AddedLocation Set-ItemProperty -Path "$Reg" -Name PATH –Value $NewPath salt_bootstrap_url: https://winbootstrap.saltproject.io/develop salt_bootstrap_options: '' state_top: base: "*": - foo dependencies: - name: foo path: ./tests/formula-foo - name: hubblestack_nova repo: spm package: https://spm.hubblestack.io/nova/hubblestack_nova-2016.10.1-1.spm - name: freebsd-11 driver: box: bento/freebsd-11.0 name: vagrant provisioner: salt_bootstrap_options: -X -p git -p curl -p sudo -p bash -p py27-sqlite3 state_top: base: "*": - tests.sudo dependencies: - name: foo path: ./tests/formula-foo - name: postfix repo: git source: https://github.com/salt-formulas/salt-formula-postfix - name: hubblestack_nova repo: spm package: https://spm.hubblestack.io/nova/hubblestack_nova-2016.10.1-1.spm <% end %> suites: - name: apt provisioner: salt_install: apt includes: - debian-9 - ubuntu-18.04 - ubuntu-16.04 - name: yum provisioner: salt_install: yum init_environment: | sudo mkdir -p /tmp/kitchen/var/cache/salt/master sudo yum install -y git includes: - centos-7 - name: bootstrap <% if @vagrant != false %> excludes: - freebsd-11 - windows-2012r2 <% end %> - name: pip provisioner: salt_install: pip <% if @vagrant != false %> excludes: - freebsd-11 - windows-2012r2 - name: vagrant includes: - freebsd-11 - windows-2012r2 <% end %> verifier: name: shell remote_exec: false command: pytest -v tests/integration/ kitchen-salt-0.7.0/.yardopts000066400000000000000000000002561402015716100157520ustar00rootroot00000000000000--readme README.rdoc --markup markdown --markup-provider maruku --charset utf-8 --title 'KitchenSalt Documentation' --output-dir html/ -t default 'lib/**/*.rb' - 'docs/*.md' kitchen-salt-0.7.0/CHANGE.md000066400000000000000000000015541402015716100153550ustar00rootroot00000000000000v0.0.19 * Added ability to copy additional dependency formulas into the VM See https://github.com/saltstack/kitchen-salt/blob/master/provisioner_options.md#dependencies v0.0.18 * Added ability to filter paths from the set of files copied to the guest See https://github.com/saltstack/kitchen-salt/blob/master/provisioner_options.md#salt_copy_filter * Added is_file_root flag - treat this project as a complete file_root. See https://github.com/saltstack/kitchen-salt/blob/master/provisioner_options.md#is_file_root * Pillar data specified using the pillars-from-files option are no longer passed through YAML.load/.to_yaml This was causing subtle data transformations with unexpected results when the reuslting yaml was consumed by salt * Added "Data failed to compile" and "No matching sls found for" to strings we watch salt-call output for signs of failure kitchen-salt-0.7.0/Dockerfile000066400000000000000000000002311402015716100160670ustar00rootroot00000000000000FROM alpine:latest RUN apk --update add docker python3-dev python3 git ruby-bundler ruby-rdoc ruby-dev gcc ruby-dev make libc-dev openssl-dev libffi-dev kitchen-salt-0.7.0/Gemfile000066400000000000000000000004021402015716100153700ustar00rootroot00000000000000source 'https://rubygems.org' group :vagrant do gem 'vagrant-wrapper' gem 'kitchen-vagrant' end group :docker do gem 'kitchen-docker' end group :git do gem 'git' end group :windows do gem 'winrm', '~>2.0' gem 'winrm-fs', '~>1.0' end gemspec kitchen-salt-0.7.0/LICENSE000066400000000000000000000021051402015716100151040ustar00rootroot00000000000000MIT LICENSE Copyright (c) Simon McCartney Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. kitchen-salt-0.7.0/README.md000066400000000000000000000013071402015716100153610ustar00rootroot00000000000000# kitchen-salt # [![Gem Version](https://badge.fury.io/rb/kitchen-salt.svg)](https://badge.fury.io/rb/kitchen-salt) [![Gem Downloads](https://ruby-gem-downloads-badge.herokuapp.com/kitchen-salt?type=total&color=brightgreen)](https://rubygems.org/gems/kitchen-salt) [![Build Status](https://github.com/saltstack/kitchen-salt/workflows/Tests/badge.svg)](https://github.com/saltstack/kitchen-salt/actions) A Test Kitchen Provisioner for Salt The provider works by generating a salt-minion config, creating pillars based on attributes in .kitchen.yml and calling salt-call. This provisioner is tested with kitchen-docker against CentOS, Ubuntu, and Debian. ## Documentation ## kitchen-salt-0.7.0/README.rdoc000066400000000000000000000030111402015716100157020ustar00rootroot00000000000000 # kitchen-salt # [![Gem Version](https://badge.fury.io/rb/kitchen-salt.svg)](https://badge.fury.io/rb/kitchen-salt) [![Gem Downloads](https://ruby-gem-downloads-badge.herokuapp.com/kitchen-salt?type=total&color=brightgreen)](https://rubygems.org/gems/kitchen-salt) [![Build Status](https://github.com/saltstack/kitchen-salt/workflows/Tests/badge.svg)](https://github.com/saltstack/kitchen-salt/actions) A Test Kitchen Provisioner for Salt The provider works by generating a salt-minion config, creating pillars based on attributes in .kitchen.yml and calling salt-call. This provisioner is tested with kitchen-docker against CentOS, Ubuntu, and Debian. ## Installation and Setup ## You'll need the test-kitchen and kitchen-salt gem's installed in your system, along with kitchen-vagrant or some other suitable driver for test-kitchen. Please see the {file:docs/gettingstarted.md}. ## Provisioner Options ## More details on all the configuration options are in {file:docs/provisioner_options.md} ## Requirements ## You'll need a driver box that is supported by the SaltStack [bootstrap](https://github.com/saltstack/salt-bootstrap) system. ## Continuous Integration and Testing PR's and other changes should validated using Github Actions, kitchen-docker, multiple state dependencies, the modified version of kitchen-salt and the latest version of test-kitchen. ## Releasing ## # hack. work. test. git add stuff git commit -v gem bump --release --tag kitchen-salt-0.7.0/Rakefile000066400000000000000000000030211402015716100155420ustar00rootroot00000000000000require 'bundler/setup' require 'bundler/gem_tasks' require 'rake' require 'rake/testtask' require 'yard' Rake::Task.define_task(:environment) desc 'Run Test Kitchen integration tests' namespace :integration do desc 'Run integration tests with kitchen-docker' task :verify, [:taskname] => [:environment] do |task, args| args.with_defaults(taskname: 'all') require 'kitchen' Kitchen.logger = Kitchen.default_file_logger @loader = Kitchen::Loader::YAML.new(local_config: '.kitchen.docker.yml') Kitchen::Config.new(loader: @loader).instances.each do |instance| if args.taskname == 'all' or instance.name.include?(args.taskname) instance.verify() end end end desc 'destroy instances with kitchen-docker' task :destroy, [:taskname] => [:environment] do |task, args| args.with_defaults(taskname: 'all') require 'kitchen' Kitchen.logger = Kitchen.default_file_logger @loader = Kitchen::Loader::YAML.new(local_config: '.kitchen.docker.yml') Kitchen::Config.new(loader: @loader).instances.each do |instance| if args.taskname == 'all' or instance.name.include?(args.taskname) instance.destroy() end end end desc 'default task' task :test, [:taskname] => [:environment] do |task, args| args.with_defaults(taskname: 'all') Rake::Task['integration:verify'].invoke(args.taskname) Rake::Task['integration:destroy'].invoke(args.taskname) end end desc 'Run yarddoc for the source' YARD::Rake::YardocTask.new task :default => ['integration:test'] kitchen-salt-0.7.0/assets/000077500000000000000000000000001402015716100154035ustar00rootroot00000000000000kitchen-salt-0.7.0/assets/install.sh000077500000000000000000000075621402015716100174220ustar00rootroot00000000000000#!/bin/sh ## ## modified chef install script ## for kitchen-salt, the entirety of chef is not required ## but ruby and gem are required in a specific directory ## for kitchen tests to run ## os="$(uname -s)" packages="ruby ruby-dev git" # install_deps PLATFORM # PLATFORM is "redhat", "debian", "freebsd", "arch", etc. install_deps() { echo "Installing dependencies" case "$1" in "redhat") echo "installing with yum..." packages="ruby ruby-devel git" # shellcheck disable=SC2086 yum install -y $packages ;; "debian") echo "installing with apt..." # shellcheck disable=SC2086 apt-get install -y $packages ;; "alpine") echo "installing with apk..." # shellcheck disable=SC2086 apk add $packages ;; "arch") echo "installing with pacman..." packages="ruby git" # shellcheck disable=SC2086 pacman -Sy --noconfirm $packages # required as otherwise gems will be installed in user's directories echo "gem: --no-user-install" > /etc/gemrc ;; "osx") echo "installing with brew..." # shellcheck disable=SC2086 brew install $packages ;; "rvm") echo "installing with rvm..." gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 # shellcheck disable=SC1001 \curl -sSL https://get.rvm.io | bash -s stable --ruby ;; "freebsd") echo "installing with pkg..." packages="ruby devel/ruby-gems git" env ASSUME_ALWAYS_YES=YES export ASSUME_ALWAYS_YES=YES # shellcheck disable=SC2086 pkg install -y $packages ;; *) echo "Unknown platform: $platform" exit 1 ;; esac if test $? -ne 0; then echo "Installation failed" report_bug exit 1 fi command -v ruby # make links to binaries mkdir -p /opt/chef/embedded/bin/ [ ! -e /opt/chef/embedded/bin/gem ] && ln -s "$(command -v gem)" /opt/chef/embedded/bin/ [ ! -e /opt/chef/embedded/bin/ruby ] && ln -s "$(command -v ruby)" /opt/chef/embedded/bin/ } if test -f "/etc/debian_version" || test -f "/etc/devuan_version"; then platform="debian" elif test -f "/etc/redhat-release"; then platform="$(sed 's/^\(.\+\) release.*/\1/' /etc/redhat-release | tr '[:upper:]' '[:lower:]')" if test "$platform" = "fedora"; then platform="redhat" fi if test "$platform" = "xenserver"; then platform="xenserver" else platform="redhat" fi elif test -f "/etc/arch-release"; then platform="arch" elif test -f "/etc/system-release"; then platform="$(sed 's/^\(.\+\) release.\+/\1/' /etc/system-release | tr '[:upper:]' '[:lower:]')" if test "$platform" = "amazon linux ami" || test "$platform" = "amazon linux"; then platform="redhat" fi elif test -f "etc/alpine-release"; then platform="alpine" # Apple OS X elif test -f "/usr/bin/sw_vers"; then platform="mac_os_x" elif test -f "/etc/release"; then if grep -q SmartOS /etc/release; then platform="smartos" else platform="solaris2" fi elif test -f "/etc/SuSE-release"; then if grep -q 'Enterprise' /etc/SuSE-release; then platform="sles" else platform="suse" fi elif test "x$os" = "xFreeBSD"; then platform="freebsd" elif test "x$os" = "xAIX"; then platform="aix" elif test -f "/etc/os-release"; then # shellcheck disable=SC1091 . /etc/os-release if test "x$CISCO_RELEASE_INFO" != "x"; then # shellcheck disable=SC1091 # shellcheck source=/dev/null . "$CISCO_RELEASE_INFO" fi platform=$ID elif test -f "/etc/lsb-release" && grep -q DISTRIB_ID /etc/lsb-release && ! grep -q wrlinux /etc/lsb-release; then platform="$(grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr '[:upper:]' '[:lower:]')" fi if test "x$platform" = "x"; then echo "Unable to determine platform version!" report_bug exit 1 fi # Install dependencies install_deps "$platform" exit 0 kitchen-salt-0.7.0/assets/install_with_git.sh000077700000000000000000000000001402015716100233002install.shustar00rootroot00000000000000kitchen-salt-0.7.0/docs/000077500000000000000000000000001402015716100150315ustar00rootroot00000000000000kitchen-salt-0.7.0/docs/README.md000077700000000000000000000000001402015716100177752../README.mdustar00rootroot00000000000000kitchen-salt-0.7.0/docs/example-kitchen.yml.md000066400000000000000000000026111402015716100212310ustar00rootroot00000000000000 # Example Configuration # driver: name: docker use_sudo: false privileged: true forward: - 80 transport: name: sftp provisioner: name: salt_solo salt_install: bootstrap is_file_root: true require_chef: false salt_copy_filter: - .git dependencies: - name: apache repo: git source: https://github.com/saltstack-formulas/apache-formula.git - name: mysql repo: git source: https://github.com/saltstack-formulas/mysql-formula.git - name: php repo: git source: https://github.com/saltstack-formulas/php-formula.git state_top: base: "*": - wordpress pillars: top.sls: base: "*": - wordpress pillars_from_files: wordpress.sls: pillar.example platforms: - name: centos driver_config: run_command: /usr/lib/systemd/systemd suites: - name: nitrogen provisioner: salt_bootstrap_options: -X -p git stable 2017.7 - name: carbon provisioner: salt_bootstrap_options: -X -p git stable 2016.11 verifier: name: shell remote_exec: false command: pytest -v tests/integration/ kitchen-salt-0.7.0/docs/gettingstarted.md000066400000000000000000000255671402015716100204220ustar00rootroot00000000000000 # Getting Started # This tutorial is going to be using the following repository: ## Installation ## KitchenSalt is a ruby gem, so ruby will be required. The easiest ways to get an up to date ruby version is to use [rbenv](https://github.com/rbenv/rbenv) or [rvm](https://rvm.io/). Luckily, a script installer is provided here. ### Installing Ruby ### #### rbenv #### For Mac, there is an `rbenv` package in homebrew. brew install rbenv For Linux, there is an [installer script](https://github.com/rbenv/rbenv-installer/tree/master/bin/rbenv-installer). The following instructions will allow for a global install of rbenv. 1. Install the dependencies for building ruby - centos sudo yum install -y openssl-devel readline-devel zlib-devel bzip2 gcc make git - ubuntu sudo apt update sudo apt install -y libssl-dev libreadline-dev zlib1g-dev bzip2 gcc make git 1. clone git repos and setup path sudo git clone git://github.com/rbenv/rbenv.git /usr/local/rbenv sudo mkdir /usr/local/rbenv/plugins sudo git clone git://github.com/rbenv/ruby-build.git /usr/local/rbenv/plugins/ruby-build sudo tee /etc/profile.d/rbenv.sh <<< 'export PATH="/usr/local/rbenv/plugins/ruby-build/bin:/usr/local/rbenv/bin:$PATH"' sudo tee -a /etc/profile.d/rbenv.sh <<< 'source <(rbenv init -)' now the shell needs to be restarted so that the rbenv commands are in the path, and then `rbenv init -` adds the shims path to the PATH as well. 1. Install ruby and set it as the version to use by default rbenv install 2.6.3 rbenv global 2.6.3 If gemsets are needed, the [rbenv-gemset](https://github.com/jf/rbenv-gemset) plugin can be added to the gemsets repository. #### rvm #### rvm is another method of managing ruby version. 1. Import the gpg key from gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB 1. Run the installer script \curl -sSL https://get.rvm.io | bash -s stable 1. Install and use ruby rvm use --install --create ruby@salt and rvm will manage installing all of the required packages for managing ruby versions via rvm. The `@salt` portion creates a specific gemset (`rvm help gemset`). ### Installing KitchenSalt ### There are a few things that are needed for running kitchen-salt 1. The bundler gem should be installed gem install bundler This will make the `bundle` command available. Bundler only lets uses the gems listed in the `Gemfile` in the directory tree where commands are run. 1. Create a gemfile. Now a [kitchen driver](https://docs.chef.io/config_yml_kitchen.html#drivers) will need to be choosen. This tutorial is going to use `kitchen-docker`. # Gemfile source 'https://rubygems.org' gem 'kitchen-salt' gem 'kitchen-docker' gem 'kitchen-sync' `kitchen-sync` is not required for this, but it does copy files to the container faster. 1. Run bundler to install gems. bundle install This will install all the gems from the Gemfile, and make only those gems available when commands are run with `bundle exec ` For more complicated setups where different methods of running Kitchen are required, groups can be assigned in the Gemfile. # Gemfile source 'https://rubygems.org' gem 'test-kitchen', :git => 'https://github.com/gtmanfred/test-kitchen.git' gem 'kitchen-salt', :git => 'https://github.com/saltstack/kitchen-salt.git' gem 'kitchen-sync' gem 'git' group :docker do gem 'kitchen-docker', :git => 'https://github.com/test-kitchen/kitchen-docker.git' end group :windows do gem 'vagrant-wrapper' gem 'kitchen-vagrant' gem 'winrm', '~>2.0' gem 'winrm-fs', :git => 'https://github.com/gtmanfred/winrm-fs.git' end To specify only installing certain groups, use the `--with` and `--without` arguements bundle install --with windows --without docker ## Setup ## Now the `.kitchen.yml` file needs to be setup for running tests. The following sections should be copied into the {file:docs/example-kitchen.yml.md} file in the root of the directory for the `wordpress-formula` ### driver ### The driver needs to be setup to build the test instance correctly. driver: name: docker use_sudo: false privileged: true forward: - 80 The example will be using docker. If `sudo` is required to run the docker command to build containers, then `use_sudo` should be set to True. The `privileged` options enables the container to run systemd as the Exec Command for the docker container. And lastly, port 80 will be forwarded to the host so that the tests can check that the wordpress website is running. If different platforms or different suites need to have different driver configurations, they can be set in the `driver_config`. ### transport ### Here is where the `kitchen-sync` transport is specified. transport: name: sftp If windows is being tested, `winrm-transport` will probably be required for the `winrm` transport. ### platforms ### This section is where the distributions and operating systems that will be tested are specified. Because different distributions put the systemd binary in different places, the `run_command` is specified here in the `driver_config`. platforms: - name: centos driver_config: run_command: /usr/lib/systemd/systemd ### suites ### This is the section where the different test suites are specified. Common uses will be to test different paths through the formulas or different versions of salt with the same formula. suites: - name: nitrogen provisioner: salt_bootstrap_options: -X -p git stable 2017.7 - name: carbon provisioner: salt_bootstrap_options: -X -p git stable 2016.11 Because different versions of salt are being tested, different `salt_bootstrap_options` are used. ### verifier ### The verifier is where the testing to check if the states run was successfull. verifier: name: shell remote_exec: false command: pytest -v tests/integration/ In the `wordpress-formula`, pytest is used to make http calls to the server to check that the wordpress website is up, and configured the way that it was specified. There are several other things that could be used here: - [testinfra](https://pypi.python.org/pypi/testinfra) - [busser](https://github.com/test-kitchen/busser) - [InSpec](https://www.chef.io/inspec/) - [Serverspec](http://serverspec.org/) And then basically any other unit test framework that could be run from the shell could be used to replace the one above. ### provisioner ### This is what kitchen-salt actually provides. provisioner: name: salt_solo salt_install: bootstrap is_file_root: true require_chef: false salt_copy_filter: - .git dependencies: - name: apache repo: git source: https://github.com/saltstack-formulas/apache-formula.git - name: mysql repo: git source: https://github.com/saltstack-formulas/mysql-formula.git - name: php repo: git source: https://github.com/saltstack-formulas/php-formula.git state_top: base: "*": - wordpress pillars: top.sls: base: "*": - wordpress pillars_from_files: wordpress.sls: pillar.example In this, the salt provisioner is specified by the name `salt_solo`. The [salt-bootstrap](https://github.com/saltstack/salt-bootstrap) script is used to install salt. The option `is_file_root` specifies that the top level directory of this git repository should be copied to the salt `file_root` in the test instance. Since ruby is not being used to test the instance in the verifier step, `require_chef` is set to `False`. Next, the `dependencies` are different repositories that the `wordpress-formula` depends on to be available to work correctly. The `state_top` specifies the top file that will be applied. Like the `state_top`, the `pillars` and `pillars_from_files` directives specifies different files to drop into the salt `pillar_roots` on the test instance. If there are multiple groups of state or pillar files that can be tested independently, it would probably be useful to specify the extra ones under the different suites. ## Run Tests ## Now that TestKitchen is all setup, the `wordpress-formula` can be tested and verified. First, kitchen commands that will be useful. - list: show the current state of each configured environment - create: create the test environment with ssh or winrm. - converge: run the provision command, in this case, salt_solo and the specified states - verify: run the verifier. - login: login to created environment - destroy: remove the created environment - test: run create, converge, verify, and then destroy if it all succeeds And since this formula is being tested by [pytest](https://docs.pytest.org/en/latest/), it will need to be installed. pip install -r requirements The above command will install requests and testinfra, which will also pull in the pytest dependency which is required by testinfra. Now the following commands can finally be run. bundle exec kitchen list bundle exec kitchen create bundle exec kitchen converge bundle exec kitchen verify bundle exec kitchen destroy Or to simplify it bundle exec kitchen test With either of these, test-kitchen will do the following 1. Create the instance using the method specified by the `driver`. 1. Install salt and run the specified states defined in the provisioner (This step is called converging). 1. Run the test suite verifier. 1. Destroy the instance if everything passed. If `kitchen test` was run, then the `kitchen list` command will show all the instances and what the result of their last command was. ## Run test interactivaly ## If your `converge` or `verify` step is failing, by default `kitchen` will keep your VM running, so you can login using ssh and debug things from there. To run the `state.apply` that converge is doing, run the following : kitchen login sudo salt-call --config-dir=/tmp/kitchen/etc/salt/ --log-level=debug state.apply If you are using the `minion_id` argument run : kitchen login sudo salt-call --config-dir=/tmp/kitchen/etc/salt/ --log-level=debug --id=salt-minion-id state.apply ## Closing ## This instance is now tested. For more information about `kitchen-salt` and `test-kitchen` in general please see the following links: - {file:docs/provisioner_options.md} - [Kitchen Salt](https://github.com/saltstack/kitchen-salt) - [Test Kitchen Docs](https://docs.chef.io/kitchen.html) - [KitchenCI](http://kitchen.ci/) - [Test Kitchen Github](https://github.com/test-kitchen) kitchen-salt-0.7.0/docs/jenkins.md000066400000000000000000000363311402015716100170220ustar00rootroot00000000000000 # Setting up kitchen to run tests like Jenkins # The new SaltStack Jenkins setup at https://jenkins.saltproject.io is using kitchen and the kitchen-ec2 driver to build servers for testing. The only thing that needs to be available is `ruby` and `gem` or `bundler` (`gem install bundler`). The rest of this guide will be using bundler. If you need to install ruby, there is a guide available for managed versions using rbenv or rvm available in the {file:docs/gettingstarted.md} doc. ## Testing with Docker/Vagrant ## By default, the salt `.kitchen.yml` is configured to use Docker to test Linux Distributions and Vagrant to test Windows. Note: If `Vagrant` is installed on the system, the WinRM plugin will have to be installed in order to run any of the tests vagrant plugin install vagrant-winrm `rsync` is also required. The Salt test suite uses rsync to transfer files to the test instance, because symlinks must be maintained, and neither scp nor sftp preserve them. With the base test suite in salt, kitchen-docker generates an ssh key at `.kitchen/docker_id_rsa`, this will need to be added to an ssh-agent. source <(ssh-agent) ssh-add .kitchen/docker_id_rsa ## Setting up EC2 ## There are requirements that are needed to run tests in EC2. 1. An AWS account 1. A vpc/subnet needs to be created 1. A security group on that vpc that has port 22 and port 5985 open for connecting. 1. A private key ### AWS Account ### An aws account will be required to build servers on EC2. The account will need `arn:aws:iam::aws:policy/AmazonEC2FullAccess` to be able to run all the necessary commands. An API id/key will need to be exported in the environment in which the kitchen commands will be run in order to create the servers. - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY ### VPC/Subnet setup ### A subnet will need to be created. There is not a specific requirement for the subnet, it just needs to be big enough to hold the number of test servers that is desired. Since `associate_public_ip: true` is set in the kitchen driver, this subnet does not need to assign a public ip by default. ### Security Group setup ### Make sure that the security group is attached to the vpc that is just created, and has the following ports open - 22/tcp for connecting to Linux machines - 5985/tcp for connecting to Windows machines ### Private Key setup ### Make sure the private key is either created or uploaded to the same region that the VPC is created in. Also make sure the private key is saved in a location that can be accessed and referenced on the machine where `kitchen` will be run. ## Setting up Kitchen ## The first thing that needs to be done is setup the extra kitchen files. The .kitchen.yml is templated to use files from .kitchen/ instead of the configurations in the actual file if the new files are available. In order to build on EC2, you can either store the files in .kitchen/ inside of the git repository or export environment variables to point to new files. The following files are the ones we use for Jenkins. ### .kitchen/driver.yml or SALT_KITCHEN_DRIVER ## driver: name: ec2 associate_public_ip: true aws_ssh_key_id: block_device_mappings: - device_name: /dev/sda1 ebs: delete_on_termination: true volume_size: 30 volume_type: gp2 instance_type: t2.medium interface: public # This can be set to "private" if you have everything setup to access it. region: require_chef_omnibus: true security_group_ids: - spot_price: '0.04' subnet_id: tags: created-by: test-kitchen transport: name: rsync ssh_key: ~/.ssh/.pem timeout: 30 This is the file that is being used for the ec2 driver. The following fields will need to be filled out: - aws_ssh_key_id -> This is the id of the public key that should be put on the VM - ssh_key -> The path to the ssh key that goes with the aws_ssh_key_id. - region -> This is the region that is setup to build servers in. - security_group_ids -> This is the security group to be used, it needs to have port 22 open for linux and port 5985 for windows. - subnet_id -> The subnet for the network interface that is going to be attached. ### .kitchen/platforms.yml or SALT_KITCHEN_PLATFORMS ### platforms: - name: opensuse driver: instance_type: t2.large spot_price: '0.04' image_search: owner-id: '679593333241' name: openSUSE-Leap-*-*-hvm-ssd-x86_64-*-ami-* tags: Name: kitchen-opensuse-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> transport: username: ec2-user provisioner: salt_bootstrap_options: -UX -p rsync git v<%= version %> salt_bootstrap_url: https://bootstrap.saltproject.io - name: centos-7 driver: tags: Name: kitchen-centos-7-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> - name: centos-6 driver: tags: Name: kitchen-centos-6-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> provisioner: salt_bootstrap_options: -P -p rsync -y -x python2.7 -X git v<%= version %> >/dev/null - name: fedora-26 driver: tags: Name: kitchen-fedora-26-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> provisioner: salt_bootstrap_options: -X -p rsync git v<%= version %> >/dev/null - name: fedora-27 driver: tags: Name: kitchen-fedora-27-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> provisioner: salt_bootstrap_options: -X -p rsync git v<%= version %> - name: ubuntu-17.10 driver: tags: Name: kitchen-ubuntu-1710-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> - name: ubuntu-16.04 driver: tags: Name: kitchen-ubuntu-1604-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> - name: ubuntu-14.04 driver: tags: Name: kitchen-ubuntu-1404-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> - name: debian-8 driver: tags: Name: kitchen-debian-8-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> block_device_mappings: null - name: debian-9 driver: tags: Name: kitchen-debian-9-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> block_device_mappings: null - name: arch driver: image_search: owner-id: '093273469852' name: arch-linux-hvm-*.x86_64-ebs tags: Name: kitchen-arch-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> transport: username: root provisioner: salt_bootstrap_options: -X -p rsync git v<%= version %> >/dev/null - name: windows-2016 driver: spot_price: '0.40' instance_type: t2.xlarge tags: Name: kitchen-windows-2016-<%= 10.times.map{[('a'..'z').to_a, (0..9).to_a].join[rand(36)]}.join %> retryable_tries: 120 provisioner: salt_bootstrap_url: https://winbootstrap.saltproject.io salt_bootstrap_options: -version <%= version %> -runservice false init_environment: | reg add "hklm\system\currentcontrolset\control\session manager\memory management" /v pagingfiles /t reg_multi_sz /d "d:\pagefile.sys 4096 8192" /f winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="5000"}' transport: name: winrm verifier: windows: true types: - unit coverage_xml: false save: $env:TEMP/salt-runtests.log: artifacts/logs/salt-runtests.log /salt/var/log/salt/minion: artifacts/logs/minion The above is the platform file as it exists on our Jenkins nodes, it just needs to be added to the correct place. This has all the logic in place so that the newest ami is found on Amazon when the commands are run. ## Extra verifier config files ## These file paths need to be exported as the `SALT_KITCHEN_VERIFIER` environment variable, or placed in `.kitchen/verifier.yml` in the git repository root. ### .kitchen/transport.yml ### Used to run the transport tests. (For Centos 7 and Ubuntu 16.04) verifier: name: nox sudo: true verbose: true run_destructive: true transport: tcp types: - ssh xml: /tmp/xml-unittests-output/ coverage_xml: /tmp/coverage.xml save: /tmp/xml-unittests-output: artifacts/ /tmp/coverage.xml: artifacts/coverage/coverage.xml /var/log/salt/minion: artifacts/logs/minion /tmp/salt-runtests.log: artifacts/logs/salt-runtests.log ### .kitchen/proxy.yml ### Used to run the proxy tests. (For Centos 7 and Ubuntu 16.04) verifier: name: runtests sudo: true verbose: true run_destructive: true types: - proxy xml: /tmp/xml-unittests-output/ coverage_xml: /tmp/coverage.xml save: /tmp/xml-unittests-output: artifacts/ /tmp/coverage.xml: artifacts/coverage/coverage.xml /var/log/salt/minion: artifacts/logs/minion /tmp/salt-runtests.log: artifacts/logs/salt-runtests.log ## Running Kitchen Commands ## Now that everything is all setup, kitchen commands can be run. ### Installing dependencies ### While in the `salt` git repository where all the .kitchen/ directory files have been setup. The first thing that needs to happen is the dependencies need to be installed, but only the dependencies required to run the tests bundle install --with ec2 windows --without opennebula docker This will install the following and their dependencies. - test-kitchen: The base library - kitchen-salt: The saltstack provisioner - kitchen-ec2: The ec2 driver, for testing instances - kitchen-sync: The rsync transport (This is important because it preserves symlinks and is the fastest transport) - winrm: For running commands on Windows test instances - winrm-fs: For copying files to and from Windows test instances ### Exporting Credentials in Environment Variables ### KitchenEC2 uses the [aws-ruby-sdk](https://github.com/aws/aws-sdk-ruby/#configuration) so any method specified for that can be used. On Jenkins, SaltStack injects the environment variables as private variables so if they are somehow compromised, Jenkins removes the strings from the output logs. export AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" This can be stored in .bashrc or whatever other shell environments file is used. ### Last setup steps ### Make sure `rsync` is installed on the local machine. Then using an ssh-agent, add the amazon ssh key to the ssh-agent. eval "$(ssh-agent)" ssh-add ~/.ssh/aws.pem The rsync command is required for being able to copy symlinks over to the testing instances. It is also significantly faster than using `scp` or `sftp`, which could take up to 15 minutes to upload the entire testing environment to the testing instance. ### Running Commands ### Now that everything is setup, test instances can be run. Running `bundle exec kitchen list` will show all of the different test cases that are available to be run based on the different configuration template files that have been setup. [root@kitchen-slave02-dev salt-ubuntu-1604-py2]# bundle exec kitchen list Instance Driver Provisioner Verifier Transport Last Action Last Error py2-opensuse Ec2 SaltSolo Nox Rsync py2-centos-7 Ec2 SaltSolo Nox Rsync py2-centos-6 Ec2 SaltSolo Nox Rsync py2-fedora-26 Ec2 SaltSolo Nox Rsync py2-fedora-27 Ec2 SaltSolo Nox Rsync py2-ubuntu-1710 Ec2 SaltSolo Nox Rsync py2-ubuntu-1604 Ec2 SaltSolo Nox Rsync py2-ubuntu-1404 Ec2 SaltSolo Nox Rsync py2-debian-8 Ec2 SaltSolo Nox Rsync py2-debian-9 Ec2 SaltSolo Nox Rsync py2-arch Ec2 SaltSolo Nox Rsync py2-windows-2016 Ec2 SaltSolo Nox Winrm py3-opensuse Ec2 SaltSolo Nox Rsync py3-centos-7 Ec2 SaltSolo Nox Rsync py3-fedora-26 Ec2 SaltSolo Nox Rsync py3-fedora-27 Ec2 SaltSolo Nox Rsync py3-ubuntu-1710 Ec2 SaltSolo Nox Rsync py3-ubuntu-1604 Ec2 SaltSolo Nox Rsync py3-debian-8 Ec2 SaltSolo Nox Rsync py3-debian-9 Ec2 SaltSolo Nox Rsync py3-arch Ec2 SaltSolo Nox Rsync py3-windows-2016 Ec2 SaltSolo Nox Winrm Any of the Instances on the left can be used with kitchen. Kitchen will build the server, and store information about it in `.kitchen/.yml`. This information is used for keeping track of which servers have been built and their instance ids. Part of the platform file above is tagging the instances with a `Name` tag so that they can be identified in the aws console, but an extra tag `{created-by: kitchen}` is also set so that they can easily be deleted. To build an instance use the `create` command. bundle exec kitchen create py2-centos-7 All this does is build the instance. Then the `converge` command can be used to run the `git.salt` state from [salt-jenkins](https://github.com/saltstack/salt-jenkins). bundle exec kitchen converge py2-centos-7 At any point if some command just needs to be run on the server, `login` can be used for Linux machines. bundle exec kitchen login py2-centos-7 For windows, this opens a RDP session for the windows machine, and that port will need to be added to the security group that is being used. (With powershell now available on Mac and Linux, it would be great to be able to just login to the Windows machines) Once the machine has been converged, the verifier can be used to run the test suite. bundle exec kitchen verify py2-centos-7 And when the machine is no longer useful, it can be deleted. bundle exec kitchen destroy py2-centos-7 And that is the life cycle of the testing instances in https://jenkins.saltproject.io In jenkins we go through each of the kitchen steps above, but you can use `test`, which will create, then converge, then verify, and if verify passes, the instance will be deleted. The custom nox verifier is used explicitly for the salt test suite, and will download all junit files for jenkins. kitchen-salt-0.7.0/docs/provisioner_options.md000066400000000000000000000372121402015716100215120ustar00rootroot00000000000000 # Provisioner Options # ## State Options ## ### dry_run ### default: `false` Setting this to True will run the highstate with test=True (good for testing states syntax) ### formula ### default: `nil` Name of the formula directory for finding where the state files are located. ### state_collection ### default: `nil` Directory containing salt states that is not a formula ### is_file_root ### default: `false` Treat the root directory of this project as a complete file root. Setting the `is_file_root` flag allows you to work with a directory tree that more closely resembles a built file_root on a salt-master, where you have may have multiple directories of states or formula. The project is recursively copied down to guest instance. Consider a directory that looks like this: top.sls .kitchen.yml apache/init.sls mysql/init.sls mysql/client.sls mysql/server.sls php/init.sls ... With a .kitchen.yml like this you can now test the completed collection: --- driver: name: vagrant provisioner: name: salt_solo is_file_root: true state_top: base: '*': - apache - mysql.client platforms: - name: ubuntu-12.04 suites: - name: default In this example, the apache state could use functionality from the php state etc. You're not just restricted to a single formula. ### local_salt_root ### default: `nil` When used with is_file_root, the directory specified here represents the /srv/ directory and everything will be copied there. provisioner: is_file_root: true local_salt_root: setup/ The above will require that a `salt` directory be located at `setup/salt/` with all the state files in it. To use the root directory, set `local_salt_root: '.'`. ### remote_states ### default: `nil` Note that this is not used if `state_collection` or `local_salt_root` are specified, or `is_file_root` is set to `true`. This is used for testing environments. Specify the salt states elsewhere, and then use them to deploy code from the current environment. For example, saltstack itself uses a `Salt-Jenkins` project to configure its testing environment. This is done like so: --- provisioner: name: salt_solo remote_states: repo: git name: git://github.com/saltstack/salt-jenkins.git branch: 2017.7 testingdir: /testing state_top: base: '*': - git.salt This will clone the `salt-jenkins.git` git repository to the `salt_file_root` (`/srv/salt`) directory, and then run the git.salt state. The optional `testingdir` field specifies where to create a copy of the test kitchen project's root directory. The `salt_copy_filter` provisioner option is applied during this copy to exclude any files. ### log_level ### default: `nil` The log level with which the salt-call command will be run. ### state_top ### default: `{}` Dictionary that will be turned into a top file on the test instance. suites: - name: client provisioner: state_top: base: '*': - beaver - beaver.ppa - name: server provisioner: state_top: base: '*': - beaver.server - beaver.ppa ### salt_env ### default: `base` Environment to use in minion config file for the file and pillar roots. ### state_top_from_file ### default: `false` If a top.sls file is provided in the states directory, and that is the top.sls that is to be used, then this should be set to true so that it is not overwritten by `state_top` ### vendor_path ### default: `nil` Absolute or relative path to a collection of formulas required for the states that are being tested. Example provisioner: vendor_path: ./srv/env/dev/_formulas ### vendor_repo ### default: `nil` Setup repositories for installing packaged formula dependencies. Types: - **apt**: apt repository - **ppa**: ppa repository - **spm**: spm repository TODO: add yum Examples provisioner: vendor_repo: - type: apt url: http://apt-mk.mirantis.com/trusty key_url: http://apt-mk.mirantis.com/public.gpg components: salt distribution: nightly - type: ppa name: neovim-ppa/unstable - type: spm url: https://spm.hubblestack.io/2016.7.1 name: hubblestack ### dependencies ### default: `[]` A list of hashes for installing formula depenencies into the VM. Types: - **path**: relative or absolute path to one formula - **apt**: install formula via apt package - **yum**: install formula via yum package - **spm**: install formula via spm package from http, path, or spm repository - **git**: install formula by cloning git repository Examples provisioner: dependencies: - name: foo path: ./tests/formula-foo - name: nginx repo: apt package: salt-formula-nginx - name: linux repo: git source: https://github.com/salt-formulas/salt-formula-linux.git - name: nginx repo: yum package: salt-formula-nginx - name: hubblestack_nova repo: spm package: https://spm.hubblestack.io/nova/hubblestack_nova-2016.10.1-1.spm ## Grain and Pillar Options ## ### grains ### default: `nil` This options allows grains to be set on the guest, written out to ``/etc/salt/grains`` For example, the following suite will define grains on the guest: - name: set-grains-test provisioner: salt_version: 0.16.2 grains: roles: - webserver - memcache deployment: datacenter4 cabinet: 13 cab_u: 14-15 ### pillars ### default: `{}` Dictionary of pillar files to setup on the minion. pillars: top.sls: base: '*': - testing testing.sls: python: bin: /usr/bin/python3 version: 3 ### pillars_from_files ### default: `nil` The pillars_from_files option allows for loading pillar data from another file, instead of being embedded in the .kitchen.yml. This allows the re-use of the example files or reduce the clutter in .kitchen.yml Consider the following suite definition: - name: tcp-output-external-pillar provisioner: pillars_from_files: beaver.sls: pillar.example pillars: top.sls: base: '*': - beaver And the contents of pillar.example is a normal pillar file: # defaults are set in map.jinja and can be over-ridden like this beaver: transport: stdout format: json ### pillars_from_directories ### default: `[]` An array of strings or hashes of directories that will be copied recursively to the pillar root. pillars_from_directories: - source: ../foo/bar dest: srv/pillar/baz - ../qux The result would be: {sandbox_path}/srv/pillar |- baz |- [contents of qux] ### pillar_env ### default: `nil` Environment to use in minion config file for the pillar root, if different from `salt_env`. ## Install Salt ## ### salt_install ### default: `bootstrap` Method by which salt will be installed: - **bootstrap**: install salt with the bootstrap script. - **yum**: install salt from a yum repository. - **apt**: install salt from an apt repository. - **distrib**: install the version of salt that comes with the distribution. - **ppa**: install salt from a ppa. - **false**: bypass salt installation. Except for `distrib` and `bootstrap`, most of these options will require extra configuration to make sure it fits the tests distribution version. Unless the newest version is used, then it should just work for yum and apt setups. If set to `none`, salt must already exist or provisioning will fail. This option is useful for images that have salt pre-installed. ### salt_version ### default: `latest` The desired version of salt that will be installed. For some places, this is used to set the repo to enable or what version to pass to bootstrap. This is also used to verify that the correct version of salt was installed before running the highstate. ### salt_bootstrap_url ### default: `https://bootstrap.saltproject.io` Location of the bootstrap script. This can also be a file located locally. If running a local file, `install_after_init_environment` must be set to `true`. For Windows, use the [powershell script](https://winbootstrap.saltproject.io/develop) ### salt_bootstrap_options ### default: `nil` Optional options passed to the bootstrap script. By default this gets set to the `salt_version` if nothing is specified here. For example, this could be used to install salt from the develop branch: suites: - name: use-development-branch-salt provisioner: salt_bootstrap_options: -M -N git develop Details on the various options available at the [salt-bootstrap](https://docs.saltproject.io/en/latest/topics/tutorials/salt_bootstrap.html) documentation. For the Windows Powershell script: platforms: - name: windows provisioner: salt_bootstrap_script: https://github.com/saltstack/salt-bootstrap/blob/develop/bootstrap-salt.ps1 salt_bootstrap_options: -version 2017.7.2 You can also use `%s` in any part of this, to replace with the version of salt, as specified by `salt_version` For example, below is a custom bootstrap option for centos6 requiring python2.7, here `%s` gets replaced by `2018.3` platforms: - centos-6: provisioner: salt_bootstrap_options: "-P -p git -p curl -p sudo -y -x python2.7 git %s" suites: - name: oxygen provisioner: salt_version: '2018.3' salt_install: bootstrap ### salt_apt_repo ### default: `https://repo.saltproject.io/apt/ubuntu/16.04/amd64` This should be the top level of the apt repository so that the `salt_version` can be appended to the url. ### salt_apt_repo_key ### default: `https://repo.saltproject.io/apt/ubuntu/16.04/amd64/latest/SALTSTACK-GPG-KEY.pub` The location of the apt repo key. ### salt_ppa ### default: `ppa:saltstack/salt` Specify the ppa to enable for installing. This is probably not as useful anymore now that salt is managed from the [official repos](https://repo.saltproject.io/#ubuntu) ### salt_yum_rpm_key ### default: `https://repo.saltproject.io/yum/redhat/7/x86_64/archive/%s/SALTSTACK-GPG-KEY.pub` The rpm key that should be installed for verifying signatures of the yum repo packages. ### salt_yum_repo ### default: `https://repo.saltproject.io/yum/redhat/$releasever/$basearch/archive/%s` The baseurl for the yum repository. `%s` is replaced with `salt_version`. More information on [SaltStack Package Repo](https://repo.saltproject.io/) ### salt_yum_repo_key ### default: `https://repo.saltproject.io/yum/redhat/$releasever/$basearch/archive/%s/SALTSTACK-GPG-KEY.pub` The gpg key url to the key for the yum repository file. `%s` is replaced with `salt_version`. More information on [SaltStack Package Repo](https://repo.saltproject.io/) ### salt_yum_repo_latest ### default : `https://repo.saltproject.io/yum/redhat/salt-repo-latest-2.el7.noarch.rpm` The url for the yum repository rpm. Used to install if `salt_version` is `latest`. More information on [SaltStack Package Repo](https://repo.saltproject.io/) ### pip_pkg ### default: `salt==%s` Name of the pip package to install for salt. This can be a file location or a package name from a pypi simple server. ### pip_editable ### default: `false` Install using the editable flag for pip ### pip_index_url ### default: `https://pypi.org/simple/` Path to the pypi simple index to use for installing salt. ### pip_extra_index_url ### default: `[]` List of extra index urls to fall back to if dependencies are not found on the main index. ### pip_bin ### default: `pip` pip binary in the `$PATH` or path to a pip binary to use for installing salt. ## Extra Config Options ## ### gpg_home ### default: `~/.gnupg/` Directory that the gnupg keyring is located. ### gpg_key ### default: `nil` Identifier for the gpg key to transfer to the test instance. Email or key id will work. ### init_environment ### default: `""` Commands to run prior to running salt-call ### install_after_init_environment ### default: `false` Install salt after `init_environment` is run ### bootstrap_url ### default: `https://raw.githubusercontent.com/saltstack/kitchen-salt/master/assets/install.sh` A bootstrap script used to provide Ruby (`ruby` and `ruby-dev`) required for the serverspec test runner on the guest OS. If this script is unable to setup Ruby, it will fallback to using Chef bootstrap installer (set via `chef_bootstrap_url`) ### chef_bootstrap_url ### default: `https://www.chef.io/chef/install.sh` The chef bootstrap installer, used to provide Ruby for the serverspec test runner on the guest OS. ### pre_salt_command ### default: nil Run any command just before running salt_command. If not successful, execution stops. ### require_chef ### default: `true` Install chef. This is required by the busser to run tests, if no verification driver is specified in the `.kitchen.yml` ### run_salt_call ### default: `true` If `false` bypassed the `salt-call` command execution. For cases where the guest OS is already provisioned. ### salt_call_command ### default for Windows: `c:\salt\salt-call.bat` default for others: `salt-call` Command used to invoke `salt-call`. ### salt_config ### default: `/etc/salt` Location in the sandbox where the salt configs are placed. ### salt_copy_filter ### default: `[]` List of filenames and directories to be excluded when copying to the sandbox. Example provisioner: is_file_root: true salt_copy_filter: - .git - .travis.yml ### salt_minion_extra_config ### default: `{}` Allows extra configuraiton that will be written to a minion config drop-in. Data will be written in `/tmp/kitchen/etc/salt/minion.d/99-minion.conf` Example provisioner: salt_minion_extra_config: mine_functions: test.ping: [] network.ip_addrs: interface: eth0 cidr: '10.0.0.0/8' ### salt_minion_config_dropin_files ### default: `[]` Allows reading of files outside the kitchen for files to add as minion config drop-ins. The files will be numbered in order of appearance in the array and will be written to `/tmp/kitchen/etc/salt/minion.d`. Example provisioner: salt_minion_config_include_files: - ../salt-base/master.d/nodegroups.conf - ../sea-salt/output-config.yml This would generate the following files : /tmp/kitchen/etc/salt/minion.d |- 97-nodegroups.conf |- 98-output-config.yml ### salt_minion_config ### default: `/etc/salt/minion` Location to place the minion config in the sandbox. ### salt_minion_config_template ### default: `nil` Local custom minion config template to be used in kitchen-salt. If filename ends in '.erb' it will be loaded as an Ruby ERB template, else it will be copied without modification. The default is {file:lib/kitchen/provisioner/minion.erb} ### salt_minion_id ### default: `nil` Customize salt minion_id. If none specified, the machine hostname is used. ### salt_file_root ### default: `/srv/salt` File root to use in the minion config and sandbox. ### salt_pillar_root ### default: `/srv/pillar` Pillar root to use in the minion config and sandbox. ### salt_state_top ### default: `/srv/salt/top.sls` Location to place the top file in the sandbox. ### salt_force_color ### default: `false` Pass `--force-color` to the salt-call command. kitchen-salt-0.7.0/docs/runtests.md000066400000000000000000000051071402015716100172450ustar00rootroot00000000000000 # The Runtests Verifier # The runtests verifier is used to run the SaltStack test suite's `tests/runtests.py` command. ## Runtests Options ## ### testingdir ### default: `/testing` This is the path inside of the kitchen root dir (default: /tmp/kitchen/) where the tests should be run. The .kitchen.yml in the salt repository puts the git repo at /tmp/kitchen/testing, so the default for this option is /testing. ### python_bin ### default: `python2` Python command to use to execute the test suite. Can be a relative or absolute path. ### verbose ### default: `false` Set to True to pass an extra `-v` to the test suite for more verbose output. ### run_destructive ### default: `false` Set to True to pass `--run-destructive` to the test suite. ### xml ### default: `false` Set to the path of a directory where the xml junit files should be dumped. ### coverage_xml ### default: `false` Set to the path where the coverage.xml file should be placed. ### types ### default: `[]` List of the different types of tests to run. Examples are `unit`, `integration`, `api`, etc. A `--` will be placed at the front of the option before passing it to runtests.py. ### tests ### default: `[]` List of single tests to run with `--name` or `-n`. This can also be set with a space seperated list of tests to run in the `KITCHEN_TESTS` environment variable. verifier: tests: - integration.states.test_pip.PipStateTest.test_46127_pip_env_vars or KITCHEN_TESTS='integration.states.test_pip.PipStateTest.test_46127_pip_env_vars' bundle exec kitchen verify py2-centos-7 ### transport ### default: `false` Set to `zeromq` or `tcp` to test different transport layers specifically. If not set, runtests.py defaults to using `zeromq` ### save ### default: `{}` A dictionary of files to save download to the filesystem to download once the test suite has completed. The salt verifier has the following. save: /tmp/xml-unittests-output: artifacts/ /tmp/coverage.xml: artifacts/coverage/coverage.xml /tmp/kitchen/var/log/salt/minion: artifacts/logs/minion /tmp/salt-runtests.log: artifacts/logs/salt-runtests.log ### sudo ### default: `false` Set to `true` to execute the runtests.py command with sudo. This is required if the username in the transport is not `root` ### windows ### default: `false` Set to `true` to make changes for running the windows test suite. This only runs a subset of the test suite, whitelisted by `tests/whitelist.txt` in the salt repository kitchen-salt-0.7.0/kitchen-salt.gemspec000066400000000000000000000021721402015716100200360ustar00rootroot00000000000000require File.expand_path('../lib/kitchen-salt/version', __FILE__) Gem::Specification.new do |spec| spec.name = 'kitchen-salt' spec.version = Kitchen::Salt::VERSION spec.authors = ['SaltStack Inc'] spec.email = ['daniel@gtmanfred.com'] spec.homepage = 'https://github.com/saltstack/kitchen-salt' spec.summary = 'salt provisioner for test-kitchen' spec.licenses = 'Apache-2.0' spec.description = 'salt provisioner for test-kitchen so that you can test all the things' spec.files = `git ls-files lib`.split("\n") spec.platform = Gem::Platform::RUBY spec.require_paths = ['lib'] spec.rubyforge_project = '[none]' spec.add_runtime_dependency 'hashie', '>= 3.5' spec.add_runtime_dependency 'test-kitchen', '>= 1.4' spec.add_development_dependency 'coderay' spec.add_development_dependency 'gem-release', '~> 0.7.3' spec.add_development_dependency 'kitchen-sync', '~> 2.2' spec.add_development_dependency 'maruku' spec.add_development_dependency 'pry', '~> 0.10.1' spec.add_development_dependency 'rake' spec.add_development_dependency 'yard' end kitchen-salt-0.7.0/lib/000077500000000000000000000000001402015716100146475ustar00rootroot00000000000000kitchen-salt-0.7.0/lib/kitchen-salt/000077500000000000000000000000001402015716100172355ustar00rootroot00000000000000kitchen-salt-0.7.0/lib/kitchen-salt/pillars.rb000066400000000000000000000067771402015716100212510ustar00rootroot00000000000000module Kitchen module Salt module Pillars private def prepare_pillars info("Preparing pillars into #{config[:salt_pillar_root]}") pillars = config[:pillars] if config.key?(:'pillars-from-files') info("pillars-from-files is deprecated in favor of pillars_from_files") pillars_from_files = config[:'pillars-from-files'] else pillars_from_files = config[:'pillars_from_files'] end pillars_from_directories = config[:pillars_from_directories] debug("Pillars Directories: #{pillars_from_directories}") if pillars_from_directories.any? pillars_from_directories.each do |dir| if dir.is_a?(Hash) local_pillar_path = File.expand_path(dir[:source]) sandbox_pillar_path = File.join(sandbox_path, dir[:dest]) info("Copying pillars from #{dir[:source]} to #{dir[:dest]}") else local_pillar_path = File.expand_path(dir) sandbox_pillar_path = File.join(sandbox_path, '/srv/pillar') info("Copying pillars from #{dir} to /srv/pillar") end cp_r_with_filter(local_pillar_path, sandbox_pillar_path, config[:salt_copy_filter]) end end debug("Pillars Hash: #{pillars}") if pillars.nil? && pillars_from_files.nil? if not config[:local_salt_root].nil? pillars_location = File.join(config[:local_salt_root], 'pillar') sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root]) cp_r_with_filter(pillars_location, sandbox_pillar_path, config[:salt_copy_filter]) return end return end # we get a hash with all the keys converted to symbols, salt doesn't like this # to convert all the keys back to strings again pillars = unsymbolize(pillars) debug("unsymbolized pillars hash: #{pillars}") # write out each pillar (we get key/contents pairs) prepare_pillar_files(pillars) # copy the pillars from files straight across, as YAML.load/to_yaml and # munge multiline strings unless pillars_from_files.nil? prepare_pillars_from_files(pillars_from_files) end end def prepare_pillar_files(pillars) pillars.each do |key, contents| # convert the hash to yaml pillar = contents.to_yaml # .to_yaml will produce ! '*' for a key, Salt doesn't like this either pillar.gsub!(/(!\s'\*')/, "'*'") # generate the filename sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root], key) debug("Rendered pillar yaml for #{key}:\n #{pillar}") write_raw_file(sandbox_pillar_path, pillar) end end def copy_pillar(key, srcfile) debug("Copying external pillar: #{key}, #{srcfile}") # generate the filename sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root], key) # create the directory where the pillar file will go FileUtils.mkdir_p(File.dirname(sandbox_pillar_path)) # copy the file across FileUtils.copy srcfile, sandbox_pillar_path end def prepare_pillars_from_files(pillars) external_pillars = unsymbolize(pillars) debug("external_pillars (unsymbolize): #{external_pillars}") external_pillars.each do |key, srcfile| copy_pillar(key, srcfile) end end end end end kitchen-salt-0.7.0/lib/kitchen-salt/states.rb000066400000000000000000000111501402015716100210630ustar00rootroot00000000000000module Kitchen module Salt module States private def prepare_state_top info('Preparing state_top') sandbox_state_top_path = File.join(sandbox_path, config[:salt_state_top]) if config[:state_top_from_file] == false # use the top.sls embedded in .kitchen.yml # we get a hash with all the keys converted to symbols, salt doesn't like this # to convert all the keys back to strings again state_top_content = unsymbolize(config[:state_top]).to_yaml # .to_yaml will produce ! '*' for a key, Salt doesn't like this either state_top_content.gsub!(/(!\s'\*')/, "'*'") else # load a top.sls from disk if config[:local_salt_root].nil? top_file = 'top.sls' unless config[:state_collection].nil? top_file = File.join(config[:state_collection], top_file) end else top_file = File.join(config[:local_salt_root], 'salt/top.sls') end state_top_content = File.read(top_file) end write_raw_file(sandbox_state_top_path, state_top_content) end def prepare_states if config[:state_collection] || config[:is_file_root] || !config[:local_salt_root].nil? prepare_state_collection elsif config[:remote_states] prepare_remote_states else prepare_formula config[:kitchen_root], config[:formula] prepare_vendor_states end end def prepare_vendor_states vendor_path = config[:vendor_path] unless vendor_path.nil? if Pathname.new(vendor_path).exist? Dir[File.join(vendor_path, '*')].each do |d| prepare_formula vendor_path, File.basename(d) end else # :vendor_path was set, but not valid raise UserError, "kitchen-salt: Invalid vendor_path set: #{vendor_path}" end end end def prepare_formula_dir(path, subdir) src = File.join(path, subdir) if File.directory?(src) debug("prepare_formula_dir: #{src} exists, copying..") subdir_path = File.join(sandbox_path, config[:salt_file_root], subdir) FileUtils.mkdir_p(subdir_path) cp_r_with_filter(src, subdir_path, config[:salt_copy_filter]) else debug("prepare_formula_dir: #{src} doesn't exist, skipping.") end end def prepare_formula(path, formula) info("Preparing formula: #{formula} from #{path}") debug("Using config #{config}") formula_dir = File.join(sandbox_path, config[:salt_file_root], formula) FileUtils.mkdir_p(formula_dir) cp_r_with_filter(File.join(path, formula), formula_dir, config[:salt_copy_filter]) # copy across the _modules etc directories for python implementation %w(_modules _states _grains _renderers _returners _runners _utils).each do |extrapath| prepare_formula_dir(path, extrapath) end end def prepare_state_collection info('Preparing state collection') collection_name = config[:collection_name] formula = config[:formula] if collection_name.nil? && formula.nil? info('neither collection_name or formula have been set, assuming this is a pre-built collection') collection_name = '' elsif collection_name.nil? collection_name = formula end if config[:local_salt_root].nil? states_location = config[:kitchen_root] unless config[:state_collection].nil? states_location = File.join(states_location, config[:state_collection]) end else states_location = File.join(config[:local_salt_root], 'salt') end collection_dir = File.join(sandbox_path, config[:salt_file_root], collection_name) FileUtils.mkdir_p(collection_dir) cp_r_with_filter(states_location, collection_dir, config[:salt_copy_filter]) end def prepare_remote_states info('Preparing remote states') remoteconf = config[:remote_states] if remoteconf[:repo] == 'git' debug("Cloning #{remoteconf[:name]}") require 'git' repo = Git.clone(remoteconf[:name], File.join(sandbox_path, config[:salt_file_root]), opts={:branch => remoteconf[:branch]}) end if remoteconf[:testingdir] dest = File.join(sandbox_path, remoteconf[:testingdir]) FileUtils.mkdir_p(dest) cp_r_with_filter(config[:kitchen_root], dest, config[:salt_copy_filter]) end end end end end kitchen-salt-0.7.0/lib/kitchen-salt/util.rb000066400000000000000000000043531402015716100205440ustar00rootroot00000000000000require 'find' module Kitchen module Salt module Util private def unsymbolize(obj) if obj.is_a? Hash obj.each_with_object({}) do |(k, v), a| a[k.to_s] = unsymbolize(v) a end elsif obj.is_a? Array obj.each_with_object([]) do |e, a| a << unsymbolize(e) a end else obj end end def cp_r_with_filter(source_paths, target_path, filter = []) debug("cp_r_with_filter:source_paths = #{source_paths}") debug("cp_r_with_filter:target_path = #{target_path}") debug("cp_r_with_filter:filter = #{filter}") Array(source_paths).each do |source_path| _cp_r_with_filter(source_path, target_path, filter) end end def _cp_r_with_filter(source_path, target_path, filter = []) Find.find(source_path) do |source| target = source.sub(/^#{source_path}/, target_path) debug("cp_r_with_filter:source = #{source}") debug("cp_r_with_filter:target = #{target}") filtered = filter.include?(File.basename(source)) if File.directory? source if filtered debug("Found #{source} in #{filter}, pruning it from the Find") Find.prune end FileUtils.mkdir_p target unless File.exist? target FileUtils.cp_r "#{source}/.", target if File.symlink? source elsif filtered debug("Found #{source} in #{filter}, not copying file") elsif File.symlink? source FileUtils.symlink File.readlink(source), target else FileUtils.copy source, target end end end def salt_call return config[:salt_call_command] if config[:salt_call_command] return 'c:\\salt\\salt-call.bat' if windows_os? 'salt-call' end def write_raw_file(name, contents) FileUtils.mkdir_p(File.dirname(name)) File.open(name, 'wb') do |file| file.write(contents) end end def write_hash_file(name, contents) raw_contents = unsymbolize(contents).to_yaml write_raw_file(name, raw_contents) end end end end kitchen-salt-0.7.0/lib/kitchen-salt/version.rb000066400000000000000000000001041402015716100212420ustar00rootroot00000000000000module Kitchen module Salt VERSION = '0.6.3'.freeze end end kitchen-salt-0.7.0/lib/kitchen/000077500000000000000000000000001402015716100162745ustar00rootroot00000000000000kitchen-salt-0.7.0/lib/kitchen/provisioner/000077500000000000000000000000001402015716100206535ustar00rootroot00000000000000kitchen-salt-0.7.0/lib/kitchen/provisioner/99-minion.conf.erb000066400000000000000000000000311402015716100240130ustar00rootroot00000000000000<%= safe_hash.to_yaml %> kitchen-salt-0.7.0/lib/kitchen/provisioner/dependencies.erb000066400000000000000000000054441402015716100240020ustar00rootroot00000000000000<%= def install_dependencies script = '' script += <<-INSTALL test ! -e "$(dirname $0)/formula-fetch.sh" || . "$(dirname $0)/formula-fetch.sh" test ! -e "$(dirname $0)/repository-setup.sh" || . "$(dirname $0)/repository-setup.sh" export SALT_ROOT="#{config[:root_path]}/#{config[:salt_file_root]}"; export SALT_SHARE_DIR='/usr/share/salt-formulas' export DEBIAN_FRONTEND=noninteractive mkdir -p "${SALT_ROOT}"; mkdir -p "${SALT_SHARE_DIR}"; INSTALL # setup apt config[:vendor_repo].select{|x| x[:type]=='apt'}.each do |repo| id =repo[:url].gsub(/[htp:\/.]/,'') arch=repo[:arch] || '[arch=amd64]' rurl=repo[:url] comp=repo[:components] || 'main' dist=repo[:distribution] || '$DISTRIB_CODENAME' rkey=repo[:key_url] script += <<-INSTALL apt_repo_add "#{id}" "#{arch}" "#{rurl}" "#{comp}" "#{dist}" "#{rkey}"; INSTALL end # setup ppa config[:vendor_repo].select{|x| x[:type]=='ppa'}.each do |repo| script += <<-INSTALL add-apt-repository "ppa:#{repo[:name]}" -y; INSTALL end # TODO, setup yum repo # update resources config[:vendor_repo].map{|x| x[:type]}.uniq.each do |type| case type when 'apt' script += <<-INSTALL apt-get update -q; sleep 10; INSTALL when 'yum' script += <<-INSTALL yum update; sleep 10; INSTALL when 'spm' script += <<-INSTALL spm update_repo; INSTALL end end # install formulas config[:dependencies].each do |formula| if formula.key?(:repo) case formula[:repo] when 'git' script += <<-INSTALL fetchGitFormula #{formula[:source]} "#{formula[:name]}" "#{formula[:branch] || 'master'}" INSTALL when 'spm' if formula[:package].nil? script += <<-INSTALL spm -c #{config[:root_path]}/etc/salt install -y #{formula[:name]}; INSTALL else script += <<-INSTALL curl -O #{formula[:package]}; spm -c #{config[:root_path]}/etc/salt local install -y "$(basename #{formula[:package]})"; INSTALL end when 'yum' script += <<-INSTALL yum install -y #{formula[:package]||formula[:name]}; INSTALL when 'apt' script += <<-INSTALL apt-get install -y #{formula[:package]||formula[:name]}; INSTALL end elsif formula.key?(:path) prepare_formula formula[:path], formula[:name] end end script += <<-INSTALL linkFormulas "$SALT_ROOT" chown -R "${SUDO_USER:-$USER}" "${SALT_SHARE_DIR}" "${SALT_ROOT}"; echo "Content of $SALT_ROOT :"; ls -la "$SALT_ROOT"; INSTALL return script end <<-INSTALL #!/usr/bin/env bash echo "Install External Dependencies"; #{install_dependencies} INSTALL %> kitchen-salt-0.7.0/lib/kitchen/provisioner/formula-fetch.sh000077500000000000000000000064471402015716100237610ustar00rootroot00000000000000#!/bin/bash # Usage: # ./formula-fetch.sh # # Example: # GIT_FORMULAS_PATH=.vendor/formulas ./formula-fetch.sh https://github.com/salt-formulas/salt-formula-salt # -- # GIT_FORMULAS_PATH=/usr/share/salt-formulas/env/_formulas # xargs -n1 ./formula-fetch.sh < dependencies.txt # Parse git dependencies from metadata.yml # $1 - path to /metadata.yml # sample to output: # https://github.com/salt-formulas/salt-formula-git git # https://github.com/salt-formulas/salt-formula-salt salt function fetchDependencies() { METADATA="$1"; grep -E "^dependencies:" "$METADATA" >/dev/null || return 0 # shellcheck disable=SC2086 (python - "$METADATA" | while read -r dep; do fetchGitFormula $dep; done) <<-DEPS import sys,yaml for dep in yaml.load(open(sys.argv[1], "ro"))["dependencies"]: print("{source} {name}").format(**dep) DEPS } # Fetch formula from git repo # $1 - formula git repo url # $2 - formula name (optional) # $3 - branch (optional) function fetchGitFormula() { test -n "${FETCHED}" || declare -a FETCHED=() export GIT_FORMULAS_PATH=${GIT_FORMULAS_PATH:-/usr/share/salt-formulas/env/_formulas} mkdir -p "$GIT_FORMULAS_PATH" if [ -n "$1" ]; then source="$1" name="$2" test -n "$name" || name="${source//*salt-formula-}" test -z "$3" && branch=master || branch=$3 if ! [[ "${FETCHED[*]}" =~ $name ]]; then # dependency not yet fetched echo "Fetching: $name" if test -e "$GIT_FORMULAS_PATH/$name"; then pushd "$GIT_FORMULAS_PATH/$name" &>/dev/null || exit test ! -e .git || git pull -r popd &>/dev/null || exit else echo "git clone $source $GIT_FORMULAS_PATH/$name -b $branch" git clone "$source" "$GIT_FORMULAS_PATH/$name" -b "$branch" fi # install dependencies FETCHED+=("$name") if [ -e "$GIT_FORMULAS_PATH/$name/metadata.yml" ]; then fetchDependencies "$GIT_FORMULAS_PATH/$name/metadata.yml" fi fi else echo Usage: fetchGitFormula "" "[local formula directory name]" "[branch]" fi } function linkFormulas() { # OPTIONAL: Link formulas from git/pkg SALT_ROOT=$1 SALT_ENV=${2:-/usr/share/salt-formulas/env} if [[ -e "$SALT_ENV" ]]; then # form git, development versions if [[ -e "$SALT_ENV/_formulas" ]]; then GIT_FORMULA_LIST=$(find "$SALT_ENV"/_formulas -maxdepth 1 -mindepth 1 -type d) for formula in $GIT_FORMULA_LIST do name=$(basename "$formula") if [[ ! -L "$SALT_ROOT/$name" ]]; then ln -fs "$formula/$name" "$SALT_ROOT/$name" fi find "$formula" -maxdepth 1 -mindepth 1 -type d |grep -E "_(modules|states|grains|renderers|returners)" | xargs -I{} \ basename {}| xargs -I{} cp -rs "$formula"/{} "$SALT_ROOT"/ done fi # form pkgs find "$SALT_ENV" -maxdepth 1 -mindepth 1 -path "*_formulas*" -prune -o -name "*" -type d -print0| xargs -I{} -0 -n1 --no-run-if-empty basename {} | xargs -I{} --no-run-if-empty \ ln -fs "$SALT_ENV"/{} "$SALT_ROOT"/{}; fi } # detect if file is being sourced [[ "$0" != "${BASH_SOURCE[0]}" ]] || { # if executed, run implicit function fetchGitFormula "${@}" } kitchen-salt-0.7.0/lib/kitchen/provisioner/gpgkey.erb000066400000000000000000000007311402015716100226340ustar00rootroot00000000000000<%= <<-GPGKEY sh -c ' #{Util.shell_helpers} # if gpg key exists, install echo "Checking for a GPG Key" if [ -f "#{config[:root_path]}/gpgkey.txt" ]; then echo "Installing \"#{config[:root_path]}/gpgkey.txt\"" GPGKEY_DIR="#{config[:root_path]}/etc/salt/gpgkeys/" mkdir -p "$GPGKEY_DIR" chmod 0700 "$GPGKEY_DIR" gpg --allow-secret-key-import --no-default-keyring --homedir "$GPGKEY_DIR" --import < "#{config[:root_path]}/gpgkey.txt" fi' GPGKEY %> kitchen-salt-0.7.0/lib/kitchen/provisioner/install.erb000066400000000000000000000126331402015716100230200ustar00rootroot00000000000000<%= salt_install = config[:salt_install] salt_url = config[:salt_bootstrap_url] salt_version = config[:salt_version] bootstrap_options = config[:salt_bootstrap_options] % [salt_version] salt_apt_repo = config[:salt_apt_repo] salt_apt_repo_key = config[:salt_apt_repo_key] salt_ppa = config[:salt_ppa] salt_yum_rpm_key = config[:salt_yum_rpm_key] % [salt_version] salt_yum_repo = config[:salt_yum_repo] % [salt_version] salt_yum_repo_key = config[:salt_yum_repo_key] % [salt_version] salt_yum_repo_latest = config[:salt_yum_repo_latest] salt_pip_pkg = config[:pip_install] salt_pip_bin = config[:pip_bin] salt_pip_install_command = ' install' salt_pip_install_command += ' -e' if config[:pip_editable] salt_pip_install_command += ' --index-url ' + config[:pip_index_url] if config[:pip_index_url] config[:pip_extra_index_url].each do |url| salt_pip_install_command += ' --extra-index-url ' + url end <<-INSTALL sh -c ' #{Util.shell_helpers} # what version of salt is installed? command -v #{salt_call} >/dev/null 2>&1 && SALT_VERSION=$(#{salt_call} --version|cut -d " " -f 2) command -v locale-gen >/dev/null 2>&1 && #{sudo('locale-gen')} en_US.UTF-8 set +x export DEBIAN_FRONTEND=noninteractive if [ -z "${SALT_VERSION}" -a "#{salt_install}" = "bootstrap" ] then mkdir -p "#{config[:root_path]}" BOOTSTRAP="#{config[:root_path]}/bootstrap.sh" if ! test -f $BOOTSTRAP; then do_download #{salt_url} "$BOOTSTRAP" fi #{sudo('sh')} $BOOTSTRAP #{bootstrap_options} elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "pip" ] then echo "Make sure setuptools and pip are new enough" export REQUIREMENTS_FILE=$(mktemp) echo "setuptools >=30,<54.* ; python_version < '3.6'" > $REQUIREMENTS_FILE echo "setuptools >=30,!=50.*,!=51.*,!=52.*; python_version >= '3.6'" > $REQUIREMENTS_FILE echo "pip >=9,<21.0.0 ; python_version < '3.6'" >> $REQUIREMENTS_FILE echo "pip >=9 ; python_version >= '3.6'" >> $REQUIREMENTS_FILE #{sudo(salt_pip_bin)} install -r $REQUIREMENTS_FILE echo #{sudo(salt_pip_bin)} #{salt_pip_install_command} #{salt_pip_pkg} #{sudo(salt_pip_bin)} #{salt_pip_install_command} #{salt_pip_pkg} elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "apt" ] then if [ ! $(command -v lsb_release &>/dev/null) ]; then . /etc/lsb-release else DISTRIB_CODENAME=$(lsb_release -s -c) fi echo "-----> Install apt-transport-https" #{sudo('apt-get')} update #{sudo('apt-get')} install -y apt-transport-https gnupg echo "-----> Configuring apt repo for salt #{salt_version}" echo "deb #{salt_apt_repo}/#{salt_version} ${DISTRIB_CODENAME} main" | #{sudo('tee')} /etc/apt/sources.list.d/salt-#{salt_version}.list do_download #{salt_apt_repo_key} /tmp/repo.key #{sudo('apt-key')} add /tmp/repo.key #{sudo('apt-get')} update sleep 10 echo "-----> Installing salt-minion (#{salt_version})" #{sudo('apt-get')} install -y salt-minion salt-common salt-master elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "distrib" ] then #{sudo('apt-get')} update #{sudo('apt-get')} install -y salt-minion salt-master elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "ppa" ] then #{sudo('apt-add-repository')} -y #{salt_ppa} #{sudo('apt-get')} update #{sudo('apt-get')} install -y salt-minion salt-common salt-master elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "yum" ] then if [ -z "#{salt_version}" ] then echo "-----> Installing yum repo for salt latest" #{sudo('yum')} install #{salt_yum_repo_latest} else echo "-----> Installing yum repo for salt #{salt_version}" #{sudo('rpm')} --import '#{salt_yum_rpm_key}' #{sudo('tee')} /etc/yum.repos.d/saltstack.repo < Installing salt-minion (#{salt_version})" #{sudo('yum')} install -y salt-minion salt-common salt-master fi # check again, now that an install of some form should have happened command -v #{salt_call} >/dev/null 2>&1 && FULL_SALT_VERSION=$(#{salt_call} --version|cut -d " " -f 2) # extract short format of Salt version if [ ! -z "${FULL_SALT_VERSION}" ] then YEAR=$(echo "$FULL_SALT_VERSION" | cut -d "." -f1) MONTH=$(echo "$FULL_SALT_VERSION" | cut -d "." -f2) SALT_VERSION="${YEAR}.${MONTH}" fi if [ -z "${SALT_VERSION}" ] then echo "No salt-minion installed, install must have failed!!" echo "salt_install = #{salt_install}" echo "salt_url = #{salt_url}" echo "bootstrap_options = #{bootstrap_options}" echo "salt_version = #{salt_version}" echo "salt_apt_repo = #{salt_apt_repo}" echo "salt_apt_repo_key = #{salt_apt_repo_key}" echo "salt_ppa = #{salt_ppa}" echo "salt_yum_rpm_key = #{salt_yum_rpm_key}" echo "salt_yum_repo = #{salt_yum_repo}" echo "salt_yum_repo_key = #{salt_yum_repo_key}" echo "salt_yum_repo_latest = #{salt_yum_repo_latest}" exit 2 elif [ "${SALT_VERSION}" = "#{salt_version}" -o "#{salt_version}" = "latest" -o "#{salt_version}" = "${FULL_SALT_VERSION}" ] then echo "You asked for #{salt_version} and you have ${FULL_SALT_VERSION} installed, sweet!" elif [ ! -z "${SALT_VERSION}" -a "#{salt_install}" = "bootstrap" ] then echo "You asked for bootstrap install and you have got ${SALT_VERSION}, hope thats ok!" else echo "You asked for #{salt_version} and you have got ${SALT_VERSION} installed, dunno how to fix that, sorry!" exit 2 fi #{install_chef}' INSTALL %> kitchen-salt-0.7.0/lib/kitchen/provisioner/install_win.erb000066400000000000000000000032371402015716100236750ustar00rootroot00000000000000<%= salt_install = config[:salt_install] salt_url = config[:salt_bootstrap_url] salt_version = config[:salt_version] bootstrap_options = config[:salt_bootstrap_options] % [salt_version] <<-POWERSHELL if (Test-Path #{salt_call}) { $installed_version = $(#{salt_call} --version).split(' ')[1] } if (-Not $(Test-Path c:\\temp)) { New-Item -Path c:\\temp -itemtype directory } if (-Not $installed_version -And "#{salt_install}" -eq "bootstrap") { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 (New-Object net.webclient).DownloadFile("#{salt_url}", "c:\\temp\\salt_bootstrap.ps1") #{sudo('powershell')} c:\\temp\\salt_bootstrap.ps1 #{bootstrap_options} } $FULL_SALT_VERSION = $(#{salt_call} --version).split(' ')[1] if ($FULL_SALT_VERSION) { $YEAR = $FULL_SALT_VERSION.split('.')[0] $MONTH = $FULL_SALT_VERSION.split('.')[1] $SALT_VERSION = "$YEAR.$MONTH" } if (-Not $SALT_VERSION) { write-host "No salt-minion installed, install must have failed!!" write-host "salt_install = #{salt_install}" write-host "salt_url = #{salt_url}" write-host "bootstrap_options = #{bootstrap_options}" } elseif ($SALT_VERSION -eq "#{salt_version}" -Or "#{salt_version}" -eq "latest" -Or "#{salt_version}" -eq "$FULL_SALT_VERSION") { write-host "You asked for #{salt_version} and you have ${FULL_SALT_VERSION} installed, sweet!" } elseif ($SALT_VERSION -And "#{salt_install}" -eq "bootstrap") { write-host "You asked for bootstrap install and you have got $SALT_VERSION, hope that's ok!" } else { write-host "You asked for #{salt_version} and you have got $SALT_VERSION installed, dunno how to fix that, sorry!" exit 2 } #{install_chef} POWERSHELL %> kitchen-salt-0.7.0/lib/kitchen/provisioner/minion.erb000066400000000000000000000012721402015716100226400ustar00rootroot00000000000000# @markup text local: true state_top: top.sls root_dir: <%= config[:root_path] %> file_roots: <%= config[:salt_env] %>: - <%= os_join(config[:root_path], config[:salt_file_root]) %> - <%= os_join(config[:root_path], config[:salt_spm_root], 'salt') %> pillar_roots: <%= config[:pillar_env] || config[:salt_env] %>: - <%= os_join(config[:root_path], config[:salt_pillar_root]) %> - <%= os_join(config[:root_path], config[:salt_spm_root], 'pillar') %> <% if windows_os? %> file_client: local winrepo_dir: <%= os_join(config[:root_path], config[:salt_file_root], 'win', 'repo') %> winrepo_dir_ng: <%= os_join(config[:root_path], config[:salt_file_root], 'win', 'repo-ng') %> <% end %> kitchen-salt-0.7.0/lib/kitchen/provisioner/repository-setup.sh000077500000000000000000000013301402015716100245640ustar00rootroot00000000000000#!/bin/bash function apt_repo_add { id="$1" arch="$2" rurl="$3" comp="$4" dist="$5" rkey="$6" test -e /tmp/apt_repo_vendor_"${id}".key || { echo "-----> Configuring formula apt vendor_repo ${rurl}" eval "$(cat /etc/lsb-release)" if curl -k "${rkey}" -o /tmp/apt_repo_vendor_"${id}".key; then echo "deb ${arch} ${rurl} ${dist} ${comp}" | tee /etc/apt/sources.list.d/vendor-repo.list apt-key add /tmp/apt_repo_vendor_"${id}".key fi }; } # detect if file is being sourced [[ "$0" != "${BASH_SOURCE[0]}" ]] || { # if executed, run implicit function #apt_repo_add "${@}" echo 'Usage: apt_repo_add "custom id" "arch" "repo url" "components" "distribution" "repo gpg key"'; } kitchen-salt-0.7.0/lib/kitchen/provisioner/salt_solo.rb000066400000000000000000000446761402015716100232200ustar00rootroot00000000000000 # # Author:: Simon McCartney # # Copyright (C) 2013, Chris Lundquist, Simon McCartney # # 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 'fileutils' require 'hashie' require 'kitchen-salt/pillars' require 'kitchen-salt/states' require 'kitchen-salt/util' require 'kitchen/provisioner/base' require 'yaml' module Kitchen module Provisioner # Basic Salt Masterless Provisioner, based on work by # # @author Chris Lundquist () class SaltSolo < Base include Kitchen::Salt::Util include Kitchen::Salt::Pillars include Kitchen::Salt::States DEFAULT_CONFIG = { bootstrap_url: 'https://raw.githubusercontent.com/saltstack/kitchen-salt/master/assets/install.sh', cache_commands: [], chef_bootstrap_url: 'https://www.chef.io/chef/install.sh', dependencies: [], dry_run: false, gpg_home: '~/.gnupg/', gpg_key: nil, install_after_init_environment: false, is_file_root: false, local_salt_root: nil, omnibus_cachier: false, pillar_env: nil, pillars_from_directories: [], pip_bin: 'pip', pip_editable: false, pip_extra_index_url: [], pip_index_url: 'https://pypi.org/simple/', pip_pkg: 'salt==%s', remote_states: nil, require_chef: true, salt_apt_repo_key: 'https://repo.saltproject.io/apt/ubuntu/16.04/amd64/latest/SALTSTACK-GPG-KEY.pub', salt_apt_repo: 'https://repo.saltproject.io/apt/ubuntu/16.04/amd64', salt_bootstrap_options: '', salt_bootstrap_url: 'https://bootstrap.saltproject.io', salt_config: '/etc/salt', salt_copy_filter: [], salt_env: 'base', salt_file_root: '/srv/salt', salt_force_color: false, salt_enable_color: true, salt_install: 'bootstrap', salt_minion_config_dropin_files: [], salt_minion_config_template: nil, salt_minion_config: '/etc/salt/minion', salt_minion_extra_config: {}, salt_minion_id: nil, salt_pillar_root: '/srv/pillar', salt_ppa: 'ppa:saltstack/salt', salt_spm_root: '/srv/spm', salt_state_top: '/srv/salt/top.sls', salt_version: 'latest', salt_yum_repo_key: 'https://repo.saltproject.io/yum/redhat/$releasever/$basearch/archive/%s/SALTSTACK-GPG-KEY.pub', salt_yum_repo_latest: 'https://repo.saltproject.io/yum/redhat/salt-repo-latest-2.el7.noarch.rpm', salt_yum_repo: 'https://repo.saltproject.io/yum/redhat/$releasever/$basearch/archive/%s', salt_yum_rpm_key: 'https://repo.saltproject.io/yum/redhat/7/x86_64/archive/%s/SALTSTACK-GPG-KEY.pub', state_collection: false, state_top_from_file: false, state_top: {}, vendor_path: nil, vendor_repo: {}, run_salt_call: true }.freeze WIN_DEFAULT_CONFIG = { chef_bootstrap_url: 'https://omnitruck.chef.io/install.ps1', salt_bootstrap_url: 'https://winbootstrap.saltproject.io/develop' }.freeze # salt-call version that supports the undocumented --retcode-passthrough command RETCODE_VERSION = '0.17.5'.freeze DEFAULT_CONFIG.each do |k, v| default_config k, v end WIN_DEFAULT_CONFIG.each_key do |key| default_config key do |provisioner| provisioner.windows_os? ? WIN_DEFAULT_CONFIG[key] : DEFAULT_CONFIG[key] end end def install_command return unless config[:salt_install] unless config[:salt_install] == 'pip' || config[:install_after_init_environment] setup_salt end end def prepare_command cmd = '' unless windows_os? cmd += <<-CHOWN #{sudo('chown')} -R "${SUDO_USER:-$USER}" "#{config[:root_path]}/#{config[:salt_file_root]}" CHOWN end if config[:prepare_salt_environment] cmd += <<-PREPARE #{config[:prepare_salt_environment]} PREPARE end return cmd unless config[:salt_install] if config[:salt_install] == 'pip' || config[:install_after_init_environment] cmd << setup_salt end cmd end def setup_salt debug(diagnose) salt_version = config[:salt_version] # if salt_verison is set, bootstrap is being used & bootstrap_options is empty, # set the bootstrap_options string to git install the requested version if (salt_version != 'latest') && (config[:salt_install] == 'bootstrap') && config[:salt_bootstrap_options].empty? debug("Using bootstrap git to install #{salt_version}") config[:salt_bootstrap_options] = "-P git v#{salt_version}" end install_template = if windows_os? File.expand_path('./../install_win.erb', __FILE__) else File.expand_path('./../install.erb', __FILE__) end erb = ERB.new(File.read(install_template)).result(binding) debug('Install Command:' + erb.to_s) erb end def install_chef return unless config[:require_chef] chef_url = config[:chef_bootstrap_url] if windows_os? <<-POWERSHELL if (-Not $(test-path c:\\opscode\\chef)) { if (-Not $(Test-Path c:\\temp)) { New-Item -Path c:\\temp -itemtype directory } [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 (New-Object net.webclient).DownloadFile("#{chef_url}", "c:\\temp\\chef_bootstrap.ps1") write-host "-----> Installing Chef Omnibus (for busser/serverspec ruby support)" #{sudo('powershell')} c:\\temp\\chef_bootstrap.ps1 } POWERSHELL else omnibus_download_dir = config[:omnibus_cachier] ? '/tmp/vagrant-cache/omnibus_chef' : '/tmp' bootstrap_url = config[:bootstrap_url] bootstrap_download_dir = '/tmp' <<-INSTALL echo "-----> Trying to install ruby(-dev) using assets.sh from kitchen-salt" mkdir -p #{bootstrap_download_dir} if [ ! -x #{bootstrap_download_dir}/install.sh ] then do_download #{bootstrap_url} #{bootstrap_download_dir}/install.sh fi #{sudo('sh')} #{bootstrap_download_dir}/install.sh -d #{bootstrap_download_dir} if [ $? -ne 0 ] || [ ! -d "/opt/chef" ] then echo "Failed install ruby(-dev) using assets.sh from kitchen-salt" echo "-----> Fallback to Chef Bootstrap script (for busser/serverspec ruby support)" mkdir -p "#{omnibus_download_dir}" if [ ! -x #{omnibus_download_dir}/install.sh ] then #{sudo('sh')} #{omnibus_download_dir}/install.sh -d #{omnibus_download_dir} fi; fi INSTALL end end def create_sandbox super prepare_data prepare_gpg_key prepare_install prepare_minion prepare_pillars prepare_grains prepare_states prepare_state_top prepare_cache_commands # upload scripts, cached formulas, and setup system repositories prepare_dependencies end def prepare_install return unless config[:salt_install] salt_version = config[:salt_version] if config[:salt_install] == 'pip' debug('Using pip to install') if File.exist?(config[:pip_pkg]) debug('Installing with pip from sdist') sandbox_pip_path = File.join(sandbox_path, 'pip') FileUtils.mkdir_p(sandbox_pip_path) FileUtils.cp_r(config[:pip_pkg], sandbox_pip_path) config[:pip_install] = File.join(config[:root_path], 'pip', File.basename(config[:pip_pkg])) else debug('Installing with pip from download') if salt_version != 'latest' config[:pip_install] = format(config[:pip_pkg], salt_version) else config[:pip_pkg].slice!('==%s') config[:pip_install] = config[:pip_pkg] end end elsif config[:salt_install] == 'bootstrap' if File.exist?(config[:salt_bootstrap_url]) FileUtils.cp_r(config[:salt_bootstrap_url], File.join(sandbox_path, 'bootstrap.sh')) end end end def init_command debug("Initialising Driver #{name}") cmd = if windows_os? 'mkdir -Force -Path '"#{config[:root_path]}""\n" else "mkdir -p '#{config[:root_path]}';" end cmd += <<-INSTALL #{config[:init_environment]} INSTALL cmd end def os_join(*args) if windows_os? File.join(*args).tr('/', '\\') else File.join(*args) end end def salt_command salt_version = config[:salt_version] cmd = '' if windows_os? salt_config_path = config[:salt_config] cmd << "(get-content #{os_join(config[:root_path], salt_config_path, 'minion')}) -replace '\\$env:TEMP', $env:TEMP | set-content #{os_join(config[:root_path], salt_config_path, 'minion')} ;" else # install/update dependencies cmd << sudo("chmod +x #{config[:root_path]}/*.sh;") cmd << sudo("#{config[:root_path]}/dependencies.sh;") cmd << sudo("#{config[:root_path]}/gpgkey.sh;") if config[:gpg_key] salt_config_path = config[:salt_config] end if config[:pre_salt_command] cmd << "#{config[:pre_salt_command]} && " end cmd << sudo("#{salt_call}") state_output = config[:salt_minion_extra_config][:state_output] if state_output cmd << " --state-output=#{state_output}" else cmd << " --state-output=changes" end cmd << " --config-dir=#{os_join(config[:root_path], salt_config_path)}" cmd << " state.highstate" cmd << " --log-level=#{config[:log_level]}" if config[:log_level] cmd << " --id=#{config[:salt_minion_id]}" if config[:salt_minion_id] cmd << " test=#{config[:dry_run]}" if config[:dry_run] cmd << ' --force-color' if config[:salt_force_color] cmd << ' --no-color' if not config[:salt_enable_color] if "#{salt_version}" > RETCODE_VERSION || salt_version == 'latest' # hope for the best and hope it works eventually cmd << ' --retcode-passthrough' end cmd << ' 2>&1 ; exit $LASTEXITCODE' if windows_os? cmd end def run_command debug("running driver #{name}") debug(diagnose) return unless config[:run_salt_call] # config[:salt_version] can be 'latest' or 'x.y.z', 'YYYY.M.x' etc # error return codes are a mess in salt: # https://github.com/saltstack/salt/pull/11337 # Unless we know we have a version that supports --retcode-passthrough # attempt to scan the output for signs of failure if "#{config[:salt_version]}" <= RETCODE_VERSION # scan the output for signs of failure, there is a risk of false negatives fail_grep = 'grep -e Result.*False -e Data.failed.to.compile -e No.matching.sls.found.for' # capture any non-zero exit codes from the salt-call | tee pipe cmd = 'set -o pipefail ; ' << salt_command # Capture the salt-call output & exit code cmd << ' 2>&1 | tee /tmp/salt-call-output ; SC=$? ; echo salt-call exit code: $SC ;' # check the salt-call output for fail messages cmd << " (sed '/#{fail_grep}/d' /tmp/salt-call-output | #{fail_grep} ; EC=$? ; echo salt-call output grep exit code ${EC} ;" # use the non-zer exit code from salt-call, then invert the results of the grep for failures cmd << ' [ ${SC} -ne 0 ] && exit ${SC} ; [ ${EC} -eq 0 ] && exit 1 ; [ ${EC} -eq 1 ] && exit 0)' cmd else salt_command end end protected def prepare_data return unless config[:data_path] info('Preparing data') debug("Using data from #{config[:data_path]}") tmpdata_dir = File.join(sandbox_path, 'data') FileUtils.mkdir_p(tmpdata_dir) cp_r_with_filter(config[:data_path], tmpdata_dir, config[:salt_copy_filter]) end def prepare_gpg_key return unless config[:gpg_key] info('Preparing gpg_key') debug("Using gpg key: #{config[:gpg_key]}") system("gpg --homedir #{config[:gpg_home]} -o #{File.join(sandbox_path, 'gpgkey.txt')} --armor --export-secret-keys #{config[:gpg_key]}") gpg_template = File.expand_path('./../gpgkey.erb', __FILE__) erb = ERB.new(File.read(gpg_template)).result(binding) debug('Install Command:' + erb.to_s) write_raw_file(File.join(sandbox_path, 'gpgkey.sh'), erb) end def prepare_minion_base_config if config[:salt_minion_config_template] minion_template = File.expand_path(config[:salt_minion_config_template], Kitchen::Config.new.kitchen_root) else minion_template = File.expand_path('./../minion.erb', __FILE__) end minion_config_content = if File.extname(minion_template) == '.erb' ERB.new(File.read(minion_template)).result(binding) else File.read(minion_template) end # create the temporary path for the salt-minion config file debug("sandbox is #{sandbox_path}") sandbox_minion_config_path = File.join(sandbox_path, config[:salt_minion_config]) write_raw_file(sandbox_minion_config_path, minion_config_content) end def prepare_minion_extra_config minion_template = File.expand_path('./../99-minion.conf.erb', __FILE__) safe_hash = Hashie.stringify_keys(config[:salt_minion_extra_config]) minion_extra_config_content = ERB.new(File.read(minion_template)).result(binding) sandbox_dropin_path = File.join(sandbox_path, 'etc/salt/minion.d') write_raw_file(File.join(sandbox_dropin_path, '99-minion.conf'), minion_extra_config_content) end def insert_minion_config_dropins sandbox_dropin_path = File.join(sandbox_path, 'etc/salt/minion.d') FileUtils.mkdir_p(sandbox_dropin_path) config[:salt_minion_config_dropin_files].each_index do |i| filename = File.basename(config[:salt_minion_config_dropin_files][i]) index = (99 - config[:salt_minion_config_dropin_files].count + i).to_s.rjust(2, '0') file = File.expand_path(config[:salt_minion_config_dropin_files][i]) data = File.read(file) write_raw_file(File.join(sandbox_dropin_path, [index, filename].join('-')), data) end end def prepare_minion info('Preparing salt-minion') prepare_minion_base_config prepare_minion_extra_config if config[:salt_minion_extra_config].keys.any? insert_minion_config_dropins if config[:salt_minion_config_dropin_files].any? end def prepare_grains debug("Grains Hash: #{config[:grains]}") return if config[:grains].nil? info("Preparing grains into #{config[:salt_config]}/grains") # generate the filename sandbox_grains_path = File.join(sandbox_path, config[:salt_config], 'grains') debug("sandbox_grains_path: #{sandbox_grains_path}") write_hash_file(sandbox_grains_path, config[:grains]) end def prepare_dependencies # Dependency scripts are bash scripts only # Copying them clobbers the kitchen temp directory # with a file named `kitchen`. If adding Windows # support for dependencies, relocate into a # sub-directory return if windows_os? # upload scripts sandbox_scripts_path = File.join(sandbox_path, config[:salt_config], 'scripts') info("Preparing scripts into #{config[:salt_config]}/scripts") # PLACEHOLDER, git formulas might be fetched locally to temp and uploaded # setup spm spm_template = File.expand_path('./../spm.erb', __FILE__) spm_config_content = ERB.new(File.read(spm_template)).result(binding) sandbox_spm_config_path = File.join(sandbox_path, config[:salt_config], 'spm') write_raw_file(sandbox_spm_config_path, spm_config_content) spm_repos = config[:vendor_repo].select { |x| x[:type] == 'spm' }.each { |x| x[:url] }.map { |x| x[:url] } spm_repos.each do |url| id = url.gsub(/[htp:\/.]/, '') spmreposd = File.join(sandbox_path, 'etc', 'salt', 'spm.repos.d') repo_spec = File.join(spmreposd, 'spm.repo') FileUtils.mkdir_p(spmreposd) repo_content = " #{id}: url: #{url} " write_raw_file(repo_spec, repo_content) end # upload scripts %w[formula-fetch.sh repository-setup.sh].each do |script| write_raw_file(File.join(sandbox_path, script), File.read(File.expand_path("../#{script}", __FILE__))) end dependencies_script = File.expand_path('./../dependencies.erb', __FILE__) dependencies_content = ERB.new(File.read(dependencies_script)).result(binding) write_raw_file(File.join(sandbox_path, 'dependencies.sh'), dependencies_content) end def prepare_cache_commands ctx = to_hash config[:cache_commands].each do |cmd| system(cmd % ctx) if $?.exitstatus.nonzero? raise ActionFailed, "cache_command '#{cmd}' failed to execute (exit status #{$?.exitstatus})" end end end def to_hash hash = Hash.new instance_variables.each {|var| hash[var[1..-1]] = instance_variable_get(var) } hash.map{|k,v| [k.to_s.to_sym,v]}.to_h end end end end kitchen-salt-0.7.0/lib/kitchen/provisioner/spm.erb000066400000000000000000000006451402015716100221510ustar00rootroot00000000000000spm_build_dir: <%= File.join(config[:root_path]) %>/build formula_path: <%= File.join(config[:root_path], (windows_os? ? config[:salt_spm_root].gsub('/', '\\') : config[:salt_spm_root])) %>/salt pillar_path: <%= File.join(config[:root_path], (windows_os? ? config[:salt_spm_root].gsub('/', '\\') : config[:salt_spm_root])) %>/pillar root_dir: <%= config[:root_path] %> spm_build_exclude: - .git - .svn - .idea - .kitchen kitchen-salt-0.7.0/lib/kitchen/transport/000077500000000000000000000000001402015716100203305ustar00rootroot00000000000000kitchen-salt-0.7.0/lib/kitchen/transport/runtests.rb000066400000000000000000000012501402015716100225420ustar00rootroot00000000000000require 'kitchen/transport/rsync' require 'shellwords' module Kitchen module Transport class Runtests < Kitchen::Transport::Rsync class Connection < Kitchen::Transport::Rsync::Connection def execute_with_exit_code(command) if command.start_with?("sh -c") super else login = login_command() cmd = [ login.instance_variable_get("@command"), login.instance_variable_get("@arguments").join(' '), '--', command.shellescape, ].join(' ') system(cmd) $?.exitstatus end end end end end end kitchen-salt-0.7.0/lib/kitchen/verifier/000077500000000000000000000000001402015716100201075ustar00rootroot00000000000000kitchen-salt-0.7.0/lib/kitchen/verifier/nox.rb000066400000000000000000000254431402015716100212500ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require "date" require "kitchen/errors" require "kitchen/verifier/base" module Kitchen module Verifier class Nox < Kitchen::Verifier::Base kitchen_verifier_api_version 1 plugin_version Kitchen::VERSION default_config :testingdir, '/testing' default_config :tests, [] default_config :save, {} default_config :windows, nil default_config :windows do |verifier| verifier.windows_os? ? true : false end default_config :verbose, false default_config :run_destructive, false default_config :runtests, false default_config :coverage, false default_config :junitxml, false default_config :from_filenames, [] default_config :from_filenames_basename, "changed-files-list.txt" default_config :from_filenames_path, false default_config :enable_filenames, false default_config :passthrough_opts, [] default_config :output_columns, 120 default_config :sysinfo, true default_config :sys_stats, false default_config :environment_vars, {} default_config :zip_windows_artifacts, false def initialize(config = {}) super(config) debug("Running kitchen-salt nox verifier initialize method") if ENV['NOX_ENABLE_FROM_FILENAMES'] config[:enable_filenames] = true end if config[:enable_filenames] and ENV['CHANGE_TARGET'] and ENV['BRANCH_NAME'] and ENV['FORCE_FULL'] != 'true' require 'git' repo = Git.open(Dir.pwd) config[:from_filenames] = repo.diff("origin/#{ENV['CHANGE_TARGET']}", "origin/#{ENV['BRANCH_NAME']}").name_status.keys.select{|file| file.end_with?('.py')} debug("Populating `from_filenames` with: #{config[:from_filenames]}") end if config[:windows] && config[:from_filenames].any? # On windows, if the changed files list is too big, it will error. # Let's then pass an absolute path to a text file which contains the list of changed # files, one per line. config[:from_filenames_path] = "#{Dir.pwd}\\#{config[:from_filenames_basename]}" from_filenames_contents = "#{config[:from_filenames].join('\n')}" File.open(config[:from_filenames_path], "w") { |f| f.write from_filenames_contents } debug("Created #{config[:from_filenames_path]} with contents:\n#{from_filenames_contents}") end end def call(state) debug("Detected platform for instance #{instance.name}: #{instance.platform.os_type}. Config's windows setting value: #{config[:windows]}") if (ENV['ONLY_DOWNLOAD_ARTEFACTS'] || '') == '1' only_download_artefacts = true else only_download_artefacts = false end if (ENV['DONT_DOWNLOAD_ARTEFACTS'] || '') == '1' dont_download_artefacts = true else dont_download_artefacts = false end if only_download_artefacts and dont_download_artefacts error_msg = "The environment variables 'ONLY_DOWNLOAD_ARTEFACTS' or 'DONT_DOWNLOAD_ARTEFACTS' cannot be both set to '1'" error(error_msg) raise ActionFailed, error_msg end if only_download_artefacts info("[#{name}] Only downloading artefacts from instance #{instance.name} with state=#{state}") else info("[#{name}] Verify on instance #{instance.name} with state=#{state}") end root_path = (config[:windows] ? '%TEMP%\\kitchen' : '/tmp/kitchen') if ENV['KITCHEN_TESTS'] ENV['KITCHEN_TESTS'].split(' ').each{|test| config[:tests].push(test)} end if ENV['NOX_PASSTHROUGH_OPTS'] ENV['NOX_PASSTHROUGH_OPTS'].split(' ').each{|opt| config[:passthrough_opts].push(opt)} end if ENV['NOX_ENV_NAME'] noxenv = ENV['NOX_ENV_NAME'] elsif config[:runtests] == true noxenv = "runtests-zeromq" else # Default to pytest-zeromq noxenv = "pytest-zeromq" end # Is the nox env already including the Python version? if not noxenv.match(/^(.*)-([\d]{1})(\.([\d]{1}))?$/) # Nox env's are not py named, they just use the # Additionally, nox envs are parametrised to enable or disable test coverage # So, the line below becomes something like: # runtests-2(coverage=True) # pytest-3(coverage=False) suite = instance.suite.name.gsub('py', '').gsub('2', '2.7') noxenv = "#{noxenv}-#{suite}" end noxenv = "#{noxenv}(coverage=#{config[:coverage] ? 'True' : 'False'})" if noxenv.include? "pytest" tests = config[:tests].join(' ') if config[:sys_stats] sys_stats = '--sys-stats' if not config[:verbose] config[:verbose] = true end else sys_stats = '' end elsif noxenv.include? "runtests" tests = config[:tests].collect{|test| "-n #{test}"}.join(' ') sys_stats = '' end if config[:junitxml] junitxml = File.join(root_path, config[:testingdir], 'artifacts', 'xml-unittests-output') if noxenv.include? "pytest" junitxml = "--junitxml=#{File.join(junitxml, "test-results-#{DateTime.now.strftime('%Y%m%d%H%M%S.%L')}.xml")}" else junitxml = "--xml=#{junitxml}" end end # Be sure to copy the remote artifacts directory to the local machine if config[:windows] save = {'$env:KitchenTestingDir/artifacts/' => "#{Dir.pwd}"} else save = {"#{File.join(root_path, config[:testingdir], 'artifacts')}/" => "#{Dir.pwd}"} end # Hash insert order matters, that's why we define a new one and merge # the one from config save.merge!(config[:save]) command = [ 'nox', "-f #{File.join(root_path, config[:testingdir], 'noxfile.py')}", (config[:windows] ? "-e #{noxenv}" : "-e '#{noxenv}'"), '--', "--output-columns=#{config[:output_columns]}", sys_stats, (config[:sysinfo] ? '--sysinfo' : ''), (config[:junitxml] ? junitxml : ''), (config[:verbose] ? '-vv' : '-v'), (config[:run_destructive] ? '--run-destructive' : ''), config[:passthrough_opts].join(' '), ].join(' ') if tests.nil? || tests.empty? # If we're not targetting specific tests... extra_command = [] if config[:windows] extra_command.push("--names-file=#{root_path}\\testing\\tests\\whitelist.txt") if config[:from_filenames_path] extra_command.push("--from-filenames=#{root_path}\\testing\\#{config[:from_filenames_basename]}") end else if config[:from_filenames].any? extra_command.push("--from-filenames=#{config[:from_filenames].join(',')}") end end command = "#{command} #{extra_command.join(' ')}" else command = "#{command} #{tests}" end environment_vars = {} if ENV['CI'] || ENV['DRONE'] || ENV['JENKINS_URL'] environment_vars['CI'] = 1 end # Hash insert order matters, that's why we define a new one and merge # the one from config environment_vars.merge!(config[:environment_vars]) # Strip trailing whitespace command = command.rstrip if config[:windows] command = "cmd.exe /c --% \"#{command}\" 2>&1" end instance.transport.connection(state) do |conn| begin if config[:windows] conn.execute('$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")') conn.execute("$env:PythonPath = [Environment]::ExpandEnvironmentVariables(\"#{File.join(root_path, config[:testingdir])}\")") conn.execute("[Environment]::SetEnvironmentVariable(\"KitchenTestingDir\", [Environment]::ExpandEnvironmentVariables(\"#{File.join(root_path, config[:testingdir])}\"), \"Machine\")") environment_vars.each do |key, value| conn.execute("[Environment]::SetEnvironmentVariable(\"#{key}\", \"#{value}\", \"Machine\")") end else command_env = [] environment_vars.each do |key, value| command_env.push("#{key}=#{value}") end if not command_env.empty? command = "env #{command_env.join(' ')} #{command}" end begin conn.execute(sudo("chown -R $USER #{root_path}")) rescue => e error("Failed to chown #{root_path} :: #{e}") end end if not only_download_artefacts info("Running Command: #{command}") conn.execute(sudo(command)) end ensure if not dont_download_artefacts save.each do |remote, local| if config[:windows] if config[:zip_windows_artifacts] begin conn.execute("7z.exe a #{remote}artifacts.zip #{remote}") rescue => e begin info("7z.exe failed, attempting zip with powershell Compress-Archive") conn.execute("powershell Compress-Archive #{remote} #{remote}artifacts.zip -Force") rescue => e2 error("Failed to create zip: #{e2}") end end end else begin conn.execute(sudo("chmod -R +r #{remote}")) rescue => e error("Failed to chown #{remote} :: #{e}") end end begin info("Copying #{remote} to #{local}") if config[:windows] if config[:zip_windows_artifacts] conn.download(remote + "artifacts.zip", local + "/artifacts.zip") system('unzip -o artifacts.zip') system('rm artifacts.zip') end else conn.download(remote, local) end rescue => e error("Failed to copy #{remote} to #{local} :: #{e}") end end end end end if only_download_artefacts info("[#{name}] Download artefacts completed.") else debug("[#{name}] Verify completed.") end end end end end kitchen-salt-0.7.0/lib/kitchen/verifier/runtests.rb000066400000000000000000000066151402015716100223330ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require "kitchen/verifier/base" module Kitchen module Verifier class Runtests < Kitchen::Verifier::Base kitchen_verifier_api_version 1 plugin_version Kitchen::VERSION default_config :testingdir, '/testing' default_config :python_bin, 'python2' default_config :verbose, false default_config :run_destructive, false default_config :xml, false default_config :coverage_xml, false default_config :types, [] default_config :tests, [] default_config :transport, false default_config :save, {} default_config :windows, false default_config :enable_filenames, false default_config :from_filenames, [] default_config :prepend, false def call(state) info("[#{name}] Verify on instance #{instance.name} with state=#{state}") root_path = (config[:windows] ? '%TEMP%\\kitchen' : '/tmp/kitchen') if ENV['KITCHEN_TESTS'] ENV['KITCHEN_TESTS'].split(' ').each{|test| config[:tests].push(test)} end if config[:enable_filenames] and ENV['CHANGE_TARGET'] and ENV['BRANCH_NAME'] and ENV['FORCE_FULL'] != 'true' require 'git' repo = Git.open('.') config[:from_filenames] = repo.diff("origin/#{ENV['CHANGE_TARGET']}", "origin/#{ENV['BRANCH_NAME']}").name_status.keys.select{|file| file.end_with?('.py')} end command = [ (config[:prepend] ? "#{config[:prepend]}" : ''), (config[:windows] ? 'python.exe' : config[:python_bin]), File.join(root_path, config[:testingdir], '/tests/runtests.py'), '--sysinfo', '--output-columns=80', (config[:windows] && config[:tests].empty? ? "--names-file=#{root_path}\\testing\\tests\\whitelist.txt" : ''), (config[:transport] ? "--transport=#{config[:transport]}" : ''), (config[:verbose] ? '-vv' : '-v'), (config[:run_destructive] ? "--run-destructive" : ''), (config[:coverage_xml] ? "--coverage-xml=#{config[:coverage_xml]}" : ''), (config[:xml] ? "--xml=#{config[:xml]}" : ''), config[:types].collect{|type| "--#{type}"}.join(' '), config[:tests].collect{|test| "-n #{test}"}.join(' '), (config[:from_filenames].any? ? "--from-filenames=#{config[:from_filenames].join(',')}" : ''), '2>&1', ].join(' ') if config[:windows] command = "cmd.exe /c \"#{command}\" 2>&1" end info("Running Command: #{command}") instance.transport.connection(state) do |conn| begin if config[:windows] conn.execute('$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")') conn.execute("$env:PythonPath = [Environment]::ExpandEnvironmentVariables(\"#{root_path}\\testing\")") else conn.execute(sudo("chown -R $USER #{root_path}")) end conn.execute(sudo(command)) ensure config[:save].each do |remote, local| unless config[:windows] conn.execute(sudo("chmod -R +r #{remote}")) end info("Copying #{remote} to #{local}") conn.download(remote, local) end end end debug("[#{name}] Verify completed.") end end end end kitchen-salt-0.7.0/lib/kitchen/verifier/salttox.rb000066400000000000000000000052711402015716100221370ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require "kitchen/verifier/base" module Kitchen module Verifier class Salttox < Kitchen::Verifier::Base kitchen_verifier_api_version 1 plugin_version Kitchen::VERSION default_config :testingdir, '/testing' default_config :verbose, false default_config :run_destructive, false default_config :xml, false default_config :coverage_xml, false default_config :types, [] default_config :tests, [] default_config :transport, false default_config :save, {} default_config :windows, false def call(state) info("[#{name}] Verify on instance #{instance.name} with state=#{state}") root_path = (config[:windows] ? '%TEMP%\\kitchen' : '/tmp/kitchen') if ENV['KITCHEN_TESTS'] ENV['KITCHEN_TESTS'].split(' ').each{|test| config[:tests].push(test)} end command = [ 'tox -c', File.join(root_path, config[:testingdir], 'tox.ini'), "-e #{instance.suite.name}", '--', '--sysinfo', '--output-columns=80', (config[:windows] ? "--names-file=#{root_path}\\testing\\tests\\whitelist.txt" : ''), (config[:transport] ? "--transport=#{config[:transport]}" : ''), (config[:verbose] ? '-v' : ''), (config[:run_destructive] ? "--run-destructive" : ''), (config[:coverage_xml] ? "--cov=salt/ --cov-report xml:#{config[:coverage_xml]}" : ''), (config[:xml] ? "--junitxml=#{config[:xml]}" : ''), config[:types].collect{|type| "--#{type}"}.join(' '), config[:tests].join(' '), '2>&1', ].join(' ') if config[:windows] command = "cmd.exe /c \"#{command}\" 2>&1" end info("Running Command: #{command}") instance.transport.connection(state) do |conn| begin if config[:windows] conn.execute('$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")') conn.execute("$env:PythonPath = [Environment]::ExpandEnvironmentVariables(\"#{root_path}\\testing\")") else conn.execute(sudo("chown -R $USER #{root_path}")) end conn.execute(sudo(command)) ensure config[:save].each do |remote, local| unless config[:windows] conn.execute(sudo("chmod -R +r #{remote}")) end info("Copying #{remote} to #{local}") conn.download(remote, local) end end end debug("[#{name}] Verify completed.") end end end end kitchen-salt-0.7.0/lib/kitchen/verifier/tox.rb000066400000000000000000000116501402015716100212510ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require "kitchen/verifier/base" module Kitchen module Verifier class Tox < Kitchen::Verifier::Base kitchen_verifier_api_version 1 plugin_version Kitchen::VERSION default_config :testingdir, '/testing' default_config :tests, [] default_config :transport, false default_config :save, {} default_config :windows, false default_config :verbose, false default_config :run_destructive, false default_config :ssh_tests, true default_config :proxy_tests, false default_config :pytest, false default_config :coverage, false default_config :junitxml, false default_config :from_filenames, [] default_config :enable_filenames, false default_config :passthrough_opts, [] default_config :output_columns, 120 default_config :sysinfo, true def call(state) info("[#{name}] Verify on instance #{instance.name} with state=#{state}") root_path = (config[:windows] ? '%TEMP%\\kitchen' : '/tmp/kitchen') if ENV['KITCHEN_TESTS'] ENV['KITCHEN_TESTS'].split(' ').each{|test| config[:tests].push(test)} end toxenv = instance.suite.name if config[:pytest] toxenv = "#{toxenv}-pytest" tests = config[:tests].join(' ') else toxenv = "#{toxenv}-runtests" tests = config[:tests].collect{|test| "-n #{test}"}.join(' ') end if config[:coverage] toxenv = "#{toxenv}-coverage" end if config[:enable_filenames] and ENV['CHANGE_TARGET'] and ENV['BRANCH_NAME'] require 'git' repo = Git.open(Dir.pwd) config[:from_filenames] = repo.diff("origin/#{ENV['CHANGE_TARGET']}", "origin/#{ENV['BRANCH_NAME']}").name_status.keys.select{|file| file.end_with?('.py')} end if config[:junitxml] junitxml = File.join(root_path, config[:testingdir], 'artifacts', 'xml-unittests-output') if config[:pytest] junitxml = "--junitxml=#{File.join(junitxml, 'test-results.xml')}" else junitxml = "--xml=#{junitxml}" end end # Be sure to copy the remote artifacts directory to the local machine save = { "#{File.join(root_path, config[:testingdir], 'artifacts')}" => "#{Dir.pwd}/" } # Hash insert order matters, that's why we define a new one and merge # the one from config save.merge!(config[:save]) command = [ 'tox -c', File.join(root_path, config[:testingdir], 'tox.ini'), "-e #{toxenv}", '--', "--output-columns=#{config[:output_columns]}", (config[:sysinfo] ? '--sysinfo' : ''), (config[:junitxml] ? junitxml : ''), (config[:windows] ? "--names-file=#{root_path}\\testing\\tests\\whitelist.txt" : ''), (config[:transport] ? "--transport=#{config[:transport]}" : ''), (config[:verbose] ? '-vv' : '-v'), (config[:run_destructive] ? "--run-destructive" : ''), (config[:ssh_tests] ? "--ssh-tests" : ''), (config[:proxy_tests] ? "--proxy-tests" : ''), config[:passthrough_opts].join(' '), (config[:from_filenames].any? ? "--from-filenames=#{config[:from_filenames].join(',')}" : ''), tests, '2>&1', ].join(' ') if config[:windows] command = "cmd.exe /c \"#{command}\" 2>&1" end info("Running Command: #{command}") instance.transport.connection(state) do |conn| begin if config[:windows] conn.execute('$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")') conn.execute("$env:PythonPath = [Environment]::ExpandEnvironmentVariables(\"#{root_path}\\testing\")") else begin conn.execute(sudo("chown -R $USER #{root_path}")) rescue => e error("Failed to chown #{root_path} :: #{e}") end end begin conn.execute(sudo(command)) rescue => e info("Verify command failed :: #{e}") end ensure save.each do |remote, local| unless config[:windows] begin conn.execute(sudo("chmod -R +r #{remote}")) rescue => e error("Failed to chown #{remote} :: #{e}") end end begin info("Copying #{remote} to #{local}") conn.download(remote, local) rescue => e error("Failed to copy #{remote} to #{local} :: #{e}") end end end end debug("[#{name}] Verify completed.") end end end end kitchen-salt-0.7.0/test-requirements.txt000066400000000000000000000000461402015716100203420ustar00rootroot00000000000000pytest-testinfra paramiko six>=1.10.0 kitchen-salt-0.7.0/tests/000077500000000000000000000000001402015716100152435ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/.filter_hidden/000077500000000000000000000000001402015716100201215ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/.filter_hidden/test.sls000066400000000000000000000000301402015716100216140ustar00rootroot00000000000000# This should be copied kitchen-salt-0.7.0/tests/.hidden/000077500000000000000000000000001402015716100165545ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/.hidden/test.sls000066400000000000000000000000301402015716100202470ustar00rootroot00000000000000# This should be copied kitchen-salt-0.7.0/tests/formula-foo/000077500000000000000000000000001402015716100174715ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/formula-foo/FORMULA000066400000000000000000000001651402015716100205230ustar00rootroot00000000000000name: foo os: Ubuntu os_family: Debian version: 0.1 release: 0 summary: Fixture formula description: Fixture formula kitchen-salt-0.7.0/tests/formula-foo/_states/000077500000000000000000000000001402015716100211335ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/formula-foo/_states/foo.py000066400000000000000000000000001402015716100222560ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/formula-foo/foo/000077500000000000000000000000001402015716100202545ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/formula-foo/foo/init.sls000066400000000000000000000000571402015716100217440ustar00rootroot00000000000000test ping: module.run: - name: test.ping kitchen-salt-0.7.0/tests/gpgkeys/000077500000000000000000000000001402015716100167145ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/gpgkeys/test.txt000066400000000000000000000066661402015716100204520ustar00rootroot00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- lQOYBFvYa0ABCADowmpvhfdUbnokDgBj/8McK3Wb5bcsr6K9CU/qehETxL3mCr+o j9/79SXYawT2601t61bl6zixDxTuW13dHWASG0q/d7fctjaV5SKPo6mOU1LsCfpy iQm3CJEhNXpEBnRM6vUf9iQ0eCm2VI87SH7Hb0BXzZPpwyR8t/vhSNnNhWE4b9+i WhzJ1ho6f6gtnK7ExwUyZ8rW9yrClJi/WXswucaPYi3PlKwJ5rwld0JiTDZNxfIm oyHMj64Z/j7Bf5kOfb26FFbijrj5Z2oyTP8jC838wSgl2jJipeIllAapis+gHVLP HfipNzMDpa8lYKwJHhVgCICJNHa09friMJL1ABEBAAEAB/0Sv65RI2pjy65c/x+6 7lwqBboof518pP0n2vsr+P8tJ3jgcdc0Xbp+EiYlQ6GXviZzhzRtur/LS1q1RHjQ GdCmRLqFp8AQsEC/XECnb1h2FXIlfzxEQ5Obwb3m/ebGWfq+Q2LRW0NZUqCRhkgv T0yfBfAZ4n5aoxRqU2ufnv2kV0W7iUrMhqTAQaoS8DA1kNp/R8CfLn4ugWYRAUfw lZ0CtAczkAvOtkqMQZ6BBKLtMmPbKoPiU2dfKol2yhzFG2d4uyXkjVs2SFQQuwFf +rPMj7/5OPzOO+bCWP4I+IoxwCoI9wyZBi4xsdRllOZfX/dYskTozQiVB9zADxnJ Iia1BADpqE4O+D96uGxesNym28YsltIRt6rpZeIfCqlZ4EsNiktFA+OG9WRXrOlo 94aIAU70JE1MaSME37DLAvLsNGfweUQ/FjvBKPBqShaRBroCWbdF4GYzaOQ03pHv VcM4CJoQ33bzkdNLgr0VKWHHDbzlHpvYfe2MsN6O7ccRY7QFIwQA/wQg7QrUAyht c1lrw2eb0+Kul0ulAQLskvLe7kffprZV+QFrk16SnUWPD6UVV/4YN/OHJQt3AsJk 8Uhug53QfV5ANIHW8o9nAd4PhZE2wzYTYNT5Vg4OoCcgfWjVvtqhhCpc4FO06oy/ 8Y6+48NXN+xrLH0kutAeV4Ma19S2RQcD/3GD2AK0usgs5NEh5yj0DgVOSwk1oeux d6QAKHi/BegGoPPtHCdOBXQgD0PnN0BO0nD58MWH3/dHkEQ3rsNPqjMAQaHcNMLw NFVqM8+RnWop3uOamhy+djCNSONfN9DBnRZdk9wgJZKOLTLlolqIWqQZeu9vs6Q9 Fan9va4hq3seO720G3Rlc3Qga2V5IDx0ZXN0QGV4YW1wbGUuY29tPokBTgQTAQgA OAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEFC+d9qhfD7lWg29V7dTt/z Bkf5BQJb2Gu8AAoJEF7dTt/zBkf59FkH/3k+Me2inLIHqob7nRNd+e4I1yRXj3Ze r9XFQKMY081W0FQm0cuNMDVec4AoAMItuLjyduEhL8BzVWjgCICRYN6TemTqaVSO 8bHLCeUjto4+wb7LpWzGKZooqgWzfG7NryRWqDdXwLF4jFVV9yI+Y61S8QhO1gaV ui+vqQh5Vikuqq2E1HSuBvrOQpiXmB8C+zsZ43o9OFvZDNeoQudtV80AdomfKDvz hdOwa67J+3qghDwQ/4G3pfMkyKxsQAURPfkXqLfdyhfvpGQRu7HsuOsYJ6essnMO XtUen3kZOkfdI+8sZhUvtjc/nI+Wnd72Q6wphvHNQpxr7aN5cq4eBDKdA5gEW9hr QAEIAKa6xSXx25dvB1NqcjBuEFQhy5WPpcwlaUoYhjd6IuVvX4vrHVtB21D3mlsg XYY2AEvMX9i18KA/B1XSFvVBBf97J6NxBgfU0rthze/sCuvuV8uXcdeW6C+0u1SL ZiFQFO0sMAGr9PJ6JLF6pDnuLfIYbZNwJ7ccHyaENIqUG4niDh3nG3jCPAOUM168 f4Lwz9LFStmqrmQvGMint+mGarvHwZSgUcI5mDYVCFrrvUQRin7tm7aK6EweqPCz Ey5JxYZJt0nXPWeAhg60OEiamNisAmyWlBF9RwcKJ/7oa50Iz699TIZgexc19q8/ 9JgVINt8HiOudkOJ5M/oafjBEg8AEQEAAQAH+wYXDLbMmp/xlcZrEnMIv37wjGj7 n2uRXZhab03ydPjaycDTceCQoMFYh00B0Hi5Bedpi9dfb0PN2ZI9w3SU5J0G3hVJ vmslz8a5aqtzUe4j6BVkhEDYNHCdp464gslVDVahG7shO/1pZGgOhJYlyDbaGT0z 8QzEBwfsoftX2pj9gjZsgfxwclzeX+kC+xUD/7isTyg5jPlOOPvkN4PsRZNahxfV I3O3VAbwKqydY29Wo6dWbEt7CCI6wvTxe5Y8ND2doNIUtu4zpLr62WqLkcDg1f53 EgGnGk4v07yCAVWoei8gewDkBcXYhcfUyRzws5entZEzcOlJpMO5rAfwqskEAMLU 3q5aIU5qSnXwa8xDihT25SvyrMj3YPE+fpikCO+pd1X2Lnnyg24a5VjfwaVA5zUZ iJQG33NFEIey+01fvFmW/jLJVSXeNco/JEPcgQaEXJiEn7Rknrjtnk9r2aBCnoT5 BNwRODLo+n2b4k5dlphEN65Q9skT2BAXem5XbR6nBADbE0VyK1yhrBooJx8okBR3 cxZgQfoK+hJetZNA8Wvad7yvvNzTSCtB1icVHd5/qvdlB6JrBvz4Tk6mKkvv1FBg 4rUuuvvKQyFj5EhZy9T0XVZTOFwPqwon8HLSvveRutxhLVqbRAz1cJfyKrUmtlow E5cUKoP55HAJ3dSMSsqGWQP+KSEwb50M2vQuNXs4tiLXVmL5f0uOl1a0Ay1iuPMu 897m3OgheEIONCYWWtoanejatADbu4HRjRXHUX7hl6KVgFi+a9A1YMibTRu8Xb64 zow0dcQ5PUJnMYyrq+o0DclnUNZVjE/JWu34MUHLfRsgNfz/IEk/pDJedOTd8IR5 YMpG5YkBPAQYAQgAJhYhBEFC+d9qhfD7lWg29V7dTt/zBkf5BQJb2GtAAhsMBQkD wmcAAAoJEF7dTt/zBkf5MxMH/RrcGfqzTJV2TzmKixFxcVERmMDM9zeG7LvwRFku pD5mzmTETNa6ds7A90k1xgPcaV1vmMK8T/cw2+NCt1VEeFKcm2gZZPwoVMBoBcdZ wSnJcBZY9yRJh8e5mKghbiibygc7EcH6vodsvA4HiLASN7rry/KoKoj8mtmMe9CC +bRjK6kGHQYFgN6E8lK/semlQafUVHp0LO4GVAguzr/swwPu1n4Hms6UFIUxC9zi OKV6A2H8m8aBjb15gHjum5UaZ08cm6gsulWZeowr/tetMdchomAz7jGfADOQKcox 9tXm1UCgADfejkritNn7XpkaNMBB0SRkYs7xN0jPXAGScAA= =lnDG -----END PGP PRIVATE KEY BLOCK----- kitchen-salt-0.7.0/tests/integration/000077500000000000000000000000001402015716100175665ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/integration/conftest.py000066400000000000000000000021011402015716100217570ustar00rootroot00000000000000import functools import os import pytest import testinfra if os.environ.get('KITCHEN_USERNAME') == 'vagrant': if 'windows' in os.environ.get('KITCHEN_INSTANCE'): test_host = testinfra.get_host('winrm://{KITCHEN_USERNAME}:{KITCHEN_PASSWORD}@{KITCHEN_HOSTNAME}:{KITCHEN_PORT}'.format(**os.environ), no_ssl=True) else: test_host = testinfra.get_host('paramiko://{KITCHEN_USERNAME}@{KITCHEN_HOSTNAME}:{KITCHEN_PORT}'.format(**os.environ), ssh_identity_file=os.environ.get('KITCHEN_SSH_KEY')) else: test_host = testinfra.get_host('docker://{KITCHEN_USERNAME}@{KITCHEN_CONTAINER_ID}'.format(**os.environ)) @pytest.fixture def host(): return test_host @pytest.fixture def salt(): if 'windows' in os.environ.get('KITCHEN_INSTANCE'): tmpconf = r'c:\Users\vagrant\AppData\Local\Temp\kitchen\etc\salt' else: test_host.run('sudo chown -R {0} /tmp/kitchen'.format(os.environ.get('KITCHEN_USERNAME'))) tmpconf = '/tmp/kitchen/etc/salt' return functools.partial(test_host.salt, config=tmpconf) kitchen-salt-0.7.0/tests/integration/test_git.py000066400000000000000000000004241402015716100217620ustar00rootroot00000000000000import pytest import os @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on freebsd images') @pytest.mark.parametrize("pkgname", [ "git", ]) def test_pkg(host, pkgname): pkg = host.package(pkgname) assert pkg.is_installed is True kitchen-salt-0.7.0/tests/integration/test_hidden.py000066400000000000000000000013561402015716100224370ustar00rootroot00000000000000import os import pytest @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='dotfiles are ignored on windows') def test_hidden_dirs(salt): expected = ('tests/.hidden',) ignored = ('tests/.filter_hidden',) dirs = salt('cp.list_master_dirs') assert all([exp in dirs for exp in expected]) assert not any([exp in dirs for exp in ignored]) @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='dotfiles are ignored on windows') def test_hidden_sls(salt): expected = ('tests/.hidden/test.sls',) ignored = ('tests/.filter_hidden/test.sls',) dirs = salt('cp.list_master') assert all([exp in dirs for exp in expected]) assert not any([exp in dirs for exp in ignored]) kitchen-salt-0.7.0/tests/integration/test_pillar.py000066400000000000000000000004651402015716100224670ustar00rootroot00000000000000import os import pytest @pytest.mark.skipif('freebsd' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on freebsd images') @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on windows') def test_gpg_pillar(salt): assert salt('pillar.get', 'gpg:test') == 'supersecret' kitchen-salt-0.7.0/tests/integration/test_salt.py000066400000000000000000000041171402015716100221450ustar00rootroot00000000000000from __future__ import unicode_literals, print_function import os import pytest def test_ping(salt): #print(os.environ) assert salt('test.ping') is True @pytest.mark.skipif('freebsd' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on freebsd images') @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='git not installed on windows') def test_git_depends(salt): formulas = {'linux', 'git'} dirs = salt('cp.list_master_dirs') assert all([formula in dirs for formula in formulas]) @pytest.mark.skipif('centos' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on centos images') @pytest.mark.skipif('freebsd' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on freebsd images') @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='APT not supported on windows') def test_apt_depends(salt): formulas = {'nginx',} dirs = salt('cp.list_master_dirs') assert all([formula in dirs for formula in formulas]) @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='git not installed on windows') def test_postfix_depends(salt): formulas = {'postfix',} dirs = salt('cp.list_master_dirs') assert all([formula in dirs for formula in formulas]) @pytest.mark.xfail @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='spm not supported on windows') def test_spm_depends(salt): formulas = {'hubblestack_nova'} dirs = salt('cp.list_master_dirs') print(dirs) assert all([formula in dirs for formula in formulas]) def test_path_depends(salt): formulas = set(['foo',]) dirs = salt('cp.list_master_dirs') print(dirs) assert all([formula in dirs for formula in formulas]) def test_cache_command_ran(salt): files = salt('cp.list_master') print(files) assert 'cache_commands_test' in files @pytest.mark.skipif('windows' not in os.environ.get('KITCHEN_INSTANCE'), reason='Skip windows specific test') def test_path_line_endings(salt): res = salt('cp.get_file_str', 'salt://top.sls') assert res == '---\r\nbase:\r\n "*":\r\n - foo\r\n' kitchen-salt-0.7.0/tests/integration/test_user.py000066400000000000000000000005341402015716100221570ustar00rootroot00000000000000import os import pytest @pytest.mark.skipif('freebsd' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on freebsd images') @pytest.mark.skipif('windows' in os.environ.get('KITCHEN_INSTANCE'), reason='Skip on windows images') def test_jdoe(host): jdoe = host.user('jdoe') assert jdoe.name == 'jdoe' assert jdoe.home == '/home/jdoe' kitchen-salt-0.7.0/tests/pillars/000077500000000000000000000000001402015716100167115ustar00rootroot00000000000000kitchen-salt-0.7.0/tests/pillars/git.sls000066400000000000000000000004161402015716100202200ustar00rootroot00000000000000git: client: enabled: true pkgs: - git user: - user: name: jdoe email: j@doe.com linux: system: enabled: true user: jdoe: enabled: true sudo: true full_name: John Doe home: /home/jdoe kitchen-salt-0.7.0/tests/pillars/gpgtest.sls000066400000000000000000000011421402015716100211070ustar00rootroot00000000000000#!yaml|gpg gpg: test: | -----BEGIN PGP MESSAGE----- hQEMAy4Ht3F+4soKAQgAl0NN/6+4gihRtEMd9+CRa0jUm0yzF7fF83m2I7GhoV42 FKwQzzHwcOHRhcx2K2LU2Zug8b9elXfCf7REubttEI5jT27UEhJ/oJ7YZJV2sd6O Q5Xox9oHZrMy+e2wSNW+luNmVQmthJ5sMmjDh1+6lE5BCrTsNEZhtump4BkEwnI7 BlsuKco/Rj+djLsXh2A35tPtqFNozWip+6MnUZfiok605quTzPU5oxKeC+R1shjA NKGqqHLvKTPebIYd+DeAhP0tM+HjrGtbydFqH0BbWZ9cGDrqlNGbFiJQTdAIrZeR DdEJCc2Py+fHXFgsac1UE1pTtVHrekDxHH2S4Gh0IdJGATKQAxvz6pbXIk4e8TH6 czAqi4YILyXQPtJqaiqc+7Y/BtDRxIe19JDW1I1y30T2+nMWPWJB5fooH5OMsW/b LhoxVW54NQ== =hgFC -----END PGP MESSAGE----- kitchen-salt-0.7.0/tests/sudo.sls000066400000000000000000000000551402015716100167400ustar00rootroot00000000000000install sudo: pkg.latest: - name: sudo