├── .gitignore ├── test ├── cookbooks │ └── test │ │ ├── templates │ │ └── template.erb │ │ ├── metadata.rb │ │ └── recipes │ │ ├── service.rb │ │ └── default.rb ├── gemfiles │ ├── train-new.gemfile │ ├── train-old.gemfile │ └── master.gemfile ├── integration │ ├── service │ │ └── serverspec │ │ │ └── service_spec.rb │ ├── inspec │ │ └── default_spec.rb │ └── default │ │ └── serverspec │ │ └── default_spec.rb └── .kitchen.yml ├── Rakefile ├── lib ├── kitchen-kubernetes │ ├── version.rb │ └── helper.rb └── kitchen │ ├── driver │ ├── pod.yaml.erb │ └── kubernetes.rb │ ├── verifier │ └── train_kubernetes_hack.rb │ └── transport │ └── kubernetes.rb ├── Gemfile ├── rsync └── Dockerfile ├── .travis.yml ├── kitchen-kubernetes.gemspec ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | .kitchen/ 3 | -------------------------------------------------------------------------------- /test/cookbooks/test/templates/template.erb: -------------------------------------------------------------------------------- 1 | ver=<%= @chef_version %> 2 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | require 'bundler/gem_tasks' 18 | -------------------------------------------------------------------------------- /test/cookbooks/test/metadata.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | name 'test' 18 | -------------------------------------------------------------------------------- /lib/kitchen-kubernetes/version.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | 18 | module KitchenKubernetes 19 | VERSION = "1.0.0" 20 | end 21 | -------------------------------------------------------------------------------- /test/gemfiles/train-new.gemfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | eval_gemfile File.expand_path('../../../Gemfile', __FILE__) 18 | 19 | gem 'train', '>= 0.30' 20 | -------------------------------------------------------------------------------- /test/gemfiles/train-old.gemfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | eval_gemfile File.expand_path('../../../Gemfile', __FILE__) 18 | 19 | gem 'train', '< 0.30' 20 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | source 'https://rubygems.org' 18 | 19 | gemspec path: File.expand_path('..', __FILE__) 20 | 21 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 22 | -------------------------------------------------------------------------------- /rsync/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | # This is used to build the kitchenkubernetes/rsync image. 18 | 19 | FROM alpine 20 | RUN apk add --update-cache rsync 21 | ENTRYPOINT ["/bin/sh", "-c", "trap 'exit 0' TERM; sleep 2147483647 & wait"] 22 | -------------------------------------------------------------------------------- /test/integration/service/serverspec/service_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | require 'serverspec' 18 | set :backend, :exec 19 | 20 | describe command('curl http://localhost') do 21 | its(:exit_status) { is_expected.to eq 0 } 22 | its(:stdout) { is_expected.to_not match /\A\s*\Z/ } 23 | end 24 | -------------------------------------------------------------------------------- /test/gemfiles/master.gemfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | eval_gemfile File.expand_path('../../../Gemfile', __FILE__) 18 | 19 | gem 'train', github: 'chef/train' 20 | gem 'inspec', github: 'chef/inspec' 21 | gem 'kitchen-inspec', github: 'chef/kitchen-inspec' 22 | gem 'test-kitchen', github: 'test-kitchen/test-kitchen' 23 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/service.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | apt_update if platform_family?('debian') 18 | 19 | package_name = platform_family?('rhel', 'amazon', 'fedora') ? 'httpd' : 'apache2' 20 | 21 | package package_name 22 | 23 | service package_name do 24 | action [:enable, :start] 25 | end 26 | 27 | # For tests. 28 | package 'curl' 29 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | file '/testfile' do 18 | owner 'root' 19 | group 'root' 20 | mode '741' 21 | content "I am a teapot\n" 22 | end 23 | 24 | template '/testtemplate' do 25 | owner 'root' 26 | group 'root' 27 | mode '444' 28 | source 'template.erb' 29 | variables chef_version: Chef::VERSION 30 | end 31 | -------------------------------------------------------------------------------- /test/integration/inspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | describe file('/testfile') do 18 | it { is_expected.to be_a_file } 19 | it { is_expected.to be_owned_by 'root' } 20 | it { is_expected.to be_mode 0741 } 21 | its(:content) { is_expected.to eq "I am a teapot\n" } 22 | end 23 | 24 | describe file('/testtemplate') do 25 | it { is_expected.to be_a_file } 26 | it { is_expected.to be_owned_by 'root' } 27 | it { is_expected.to be_mode 0444 } 28 | its(:content) { is_expected.to match /^ver=13/ } 29 | end 30 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | require 'serverspec' 18 | set :backend, :exec 19 | 20 | describe file('/testfile') do 21 | it { is_expected.to be_a_file } 22 | it { is_expected.to be_owned_by 'root' } 23 | it { is_expected.to be_mode 741 } 24 | its(:content) { is_expected.to eq "I am a teapot\n" } 25 | end 26 | 27 | describe file('/testtemplate') do 28 | it { is_expected.to be_a_file } 29 | it { is_expected.to be_owned_by 'root' } 30 | it { is_expected.to be_mode 444 } 31 | its(:content) { is_expected.to match /^ver=13/ } 32 | end 33 | -------------------------------------------------------------------------------- /lib/kitchen-kubernetes/helper.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | 18 | module KitchenKubernetes 19 | # Utility mixin for other classes in this plugin. 20 | # 21 | # @since 1.0 22 | # @api private 23 | module Helper 24 | # Because plugins and connections have different APIs. 25 | def kube_options 26 | if defined?(config) 27 | config 28 | elsif defined?(options) 29 | options 30 | else 31 | raise "Something went wrong, please file a bug" 32 | end 33 | end 34 | 35 | def kubectl_command(*cmd) 36 | out = [kube_options[:kubectl_command]] 37 | if kube_options[:context] 38 | out << '--context' 39 | out << kube_options[:context] 40 | end 41 | out.concat(cmd) 42 | out 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /test/.kitchen.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | driver: 18 | name: kubernetes 19 | 20 | provisioner: 21 | cookbooks_path: test/cookbooks/ 22 | 23 | kitchen: 24 | test_base_path: <%= File.expand_path('../integration', __FILE__) %> 25 | 26 | platforms: 27 | - name: centos-6 28 | - name: centos-7 29 | - name: ubuntu-14.04 30 | - name: ubuntu-16.04 31 | - name: debian-7 32 | - name: debian-8 33 | - name: debian-9 34 | - name: amazonlinux-2016.09 35 | - name: amazonlinux-2017.03 36 | - name: amazonlinux-2017.09 37 | - name: fedora-25 38 | - name: fedora-26 39 | 40 | suites: 41 | - name: default 42 | run_list: 43 | - recipe[test] 44 | - name: service 45 | run_list: 46 | - recipe[test::service] 47 | driver: 48 | init_system: systemd 49 | includes: 50 | - centos-7 51 | - ubuntu-16.04 52 | - fedora-26 53 | - name: inspec 54 | run_list: 55 | - recipe[test] 56 | verifier: 57 | name: inspec 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | sudo: required 18 | language: ruby 19 | rvm: 2.4.3 20 | gemfile: 21 | - Gemfile 22 | - test/gemfiles/train-old.gemfile 23 | - test/gemfiles/train-new.gemfile 24 | - test/gemfiles/master.gemfile 25 | 26 | services: 27 | - docker 28 | 29 | env: 30 | - CHANGE_MINIKUBE_NONE_USER=true 31 | 32 | before_install: 33 | - 'if [[ $BUNDLE_GEMFILE == *master.gemfile ]]; then gem update --system; fi' 34 | - gem --version 35 | - gem install bundler 36 | - bundle --version 37 | - 'bundle config --local path ${BUNDLE_PATH:-$(dirname $BUNDLE_GEMFILE)/vendor/bundle}' 38 | install: bundle update --jobs=3 --retry=3 39 | 40 | before_script: 41 | - curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.7.0/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin 42 | - curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin 43 | - sudo minikube start --vm-driver=none --kubernetes-version=v1.8.0 44 | - minikube update-context 45 | - JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}'; until kubectl get nodes -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do sleep 1; done 46 | 47 | script: 48 | - bundle exec kitchen --version 49 | - bundle exec inspec --version 50 | - cd test/ && bundle exec kitchen test -d always 51 | -------------------------------------------------------------------------------- /kitchen-kubernetes.gemspec: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | lib = File.expand_path('../lib', __FILE__) 18 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 19 | require 'kitchen-kubernetes/version' 20 | 21 | Gem::Specification.new do |spec| 22 | spec.name = 'kitchen-kubernetes' 23 | spec.version = KitchenKubernetes::VERSION 24 | spec.authors = ['Noah Kantrowitz'] 25 | spec.email = ['noah@coderanger.net'] 26 | spec.description = %q{A Kubernetes Driver for Test Kitchen} 27 | spec.summary = spec.description 28 | spec.homepage = 'https://github.com/coderanger/kitchen-kubernetes' 29 | spec.license = 'Apache 2.0' 30 | 31 | spec.files = `git ls-files`.split($/) 32 | spec.executables = [] 33 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 34 | spec.require_paths = ['lib'] 35 | 36 | spec.add_dependency 'test-kitchen', '~> 1.18' 37 | 38 | spec.add_development_dependency 'bundler' 39 | spec.add_development_dependency 'rake' 40 | 41 | # Unit testing gems. 42 | spec.add_development_dependency 'rspec', '~> 3.2' 43 | spec.add_development_dependency 'rspec-its', '~> 1.2' 44 | spec.add_development_dependency 'fuubar', '~> 2.0' 45 | spec.add_development_dependency 'simplecov', '~> 0.9' 46 | spec.add_development_dependency 'codecov', '~> 0.0', '>= 0.0.2' 47 | 48 | # Integration testing. 49 | spec.add_development_dependency 'kitchen-inspec', '~> 0.20' 50 | end 51 | -------------------------------------------------------------------------------- /lib/kitchen/driver/pod.yaml.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | apiVersion: v1 18 | kind: Pod 19 | metadata: 20 | name: <%= pod_id %> 21 | labels: 22 | heritage: kitchen-kubernetes 23 | annotations: 24 | coderanger.net/kitchen-instance: <%= instance.name %> 25 | coderanger.net/kitchen-platform: <%= instance.platform.name %> 26 | coderanger.net/kitchen-created-by: <%= Etc.getlogin || 'unknown' %> 27 | coderanger.net/kitchen-created-on: <%= Socket.gethostname %> 28 | spec: 29 | initContainers: 30 | - name: chef 31 | image: <%= config[:chef_image] %>:<%= config[:chef_version] %> 32 | <%- if config[:chef_version] == 'latest' -%> 33 | command: ["/bin/cp", "-a", "-u", "/opt/chef", "/mnt"] 34 | <%- else -%> 35 | command: ["/bin/sh", "-c", "if [ ! -d /mnt/chef/bin ]; then cp -a /opt/chef /mnt; fi"] 36 | <%- end -%> 37 | volumeMounts: 38 | - mountPath: /mnt/chef 39 | name: chef 40 | containers: 41 | - name: default 42 | image: <%= config[:image] %> 43 | <%- if config[:image_pull_policy] %> 44 | imagePullPolicy: <%= config[:image_pull_policy] %> 45 | <%- end -%> 46 | <%- if config[:init_system] == 'systemd' -%> 47 | env: 48 | - name: container 49 | value: docker 50 | command: ["/sbin/init"] 51 | <%- else -%> 52 | command: ["/bin/sh", "-c", "trap 'exit 0' TERM; sleep 2147483647 & wait"] 53 | <%- end -%> 54 | volumeMounts: 55 | - mountPath: /opt/chef 56 | name: chef 57 | - mountPath: /tmp/kitchen 58 | name: kitchen 59 | <%- if config[:init_system] == 'systemd' -%> 60 | - mountPath: /tmp 61 | name: systemd-tmp 62 | - mountPath: /run 63 | name: systemd-run 64 | - mountPath: /run/lock 65 | name: systemd-lock 66 | - mountPath: /sys/fs/cgroup 67 | name: systemd-cgroup 68 | readOnly: true 69 | <%- end -%> 70 | - name: rsync 71 | image: <%= config[:rsync_image] %> 72 | volumeMounts: 73 | - mountPath: /tmp/kitchen 74 | name: kitchen 75 | volumes: 76 | - <%= {name: 'chef'}.merge(config[:cache_volume]).to_json %> 77 | - name: kitchen 78 | emptyDir: {} 79 | <%- if config[:init_system] == 'systemd' -%> 80 | - name: systemd-tmp 81 | emptyDir: 82 | medium: Memory 83 | - name: systemd-run 84 | emptyDir: 85 | medium: 86 | - name: systemd-lock 87 | emptyDir: 88 | medium: Memory 89 | - name: systemd-cgroup 90 | hostPath: 91 | path: /sys/fs/cgroup 92 | type: Directory 93 | <%- end -%> 94 | <%- if config[:image_pull_secrets] -%> 95 | imagePullSecrets: <%= Array(config[:image_pull_secrets]).map {|n| {name: n} }.to_json %> 96 | <%- end -%> 97 | -------------------------------------------------------------------------------- /lib/kitchen/verifier/train_kubernetes_hack.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # This is copied from https://github.com/chef/train/pull/205 until InSpec can 17 | # support it internally. The only changes are to rename with _hack and 18 | # back-compat support for the changes in the files API (LinuxFile vs File::Remote::Linux). 19 | 20 | require 'mixlib/shellout' 21 | 22 | require 'train' 23 | 24 | module Train::Transports 25 | class KubernetesHack < Train.plugin(1) 26 | name 'kubernetes_hack' 27 | 28 | include_options Train::Extras::CommandWrapper 29 | option :pod, required: true 30 | option :container, default: nil 31 | option :kubectl_path, default: 'kubectl' 32 | option :context, default: nil 33 | 34 | def connection(state = {}) 35 | opts = merge_options(options, state || {}) 36 | validate_options(opts) 37 | opts[:logger] ||= logger 38 | unless @connection && @connection_opts == opts 39 | @connection ||= Connection.new(opts) 40 | @connection_opts = opts.dup 41 | end 42 | @connection 43 | end 44 | 45 | class Connection < BaseConnection 46 | if Gem::Requirement.create('< 0.30').satisfied_by?(Gem::Version.create(Train::VERSION)) 47 | # The API for a connection changed a lot in 0.30, this is a compat shim. 48 | def os 49 | @os ||= OSCommon.new(self, family: 'unix') 50 | end 51 | 52 | def file(path) 53 | @files[path] ||= file_via_connection(path) 54 | end 55 | 56 | def run_command(cmd) 57 | run_command_via_connection(cmd) 58 | end 59 | end 60 | 61 | def file_via_connection(path) 62 | defined?(Train::File::Remote::Linux) ? Train::File::Remote::Linux.new(self, path) : LinuxFile.new(self, path) 63 | end 64 | 65 | def run_command_via_connection(cmd) 66 | kubectl_cmd = [options[:kubectl_path], 'exec'] 67 | kubectl_cmd.concat(['--context', options[:context]]) if options[:context] 68 | kubectl_cmd.concat(['--container', options[:container]]) if options[:container] 69 | kubectl_cmd.concat([options[:pod], '--', '/bin/sh', '-c', cmd]) 70 | 71 | so = Mixlib::ShellOut.new(kubectl_cmd, logger: logger) 72 | so.run_command 73 | if so.error? 74 | # Trim the "command terminated with exit code N" line from the end 75 | # of the stderr content. 76 | so.stderr.gsub!(/command terminated with exit code #{so.exitstatus}\n\Z/, '') 77 | end 78 | CommandResult.new(so.stdout, so.stderr, so.exitstatus) 79 | end 80 | 81 | def uri 82 | if options[:container] 83 | "kubernetes://#{options[:pod]}/#{options[:container]}" 84 | else 85 | "kubernetes://#{options[:pod]}" 86 | end 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/kitchen/transport/kubernetes.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | require 'shellwords' 18 | 19 | require 'kitchen/login_command' 20 | require 'kitchen/shell_out' 21 | require 'kitchen/transport/base' 22 | 23 | require 'kitchen-kubernetes/helper' 24 | 25 | 26 | module Kitchen 27 | module Transport 28 | 29 | # Kubernetes transport for Kitchen. Uses kubectl exec. 30 | # 31 | # @author Noah Kantrowitz 32 | # @since 1.0.0 33 | # @see Kitchen::Driver::Kubernetes 34 | class Kubernetes < Kitchen::Transport::Base 35 | # All configuration options can be found in the Driver class. 36 | 37 | # (see Base#connection) 38 | def connection(state, &block) 39 | # No persistent anything so no need to reuse connections. 40 | Connection.new( 41 | pod_id: state[:pod_id], 42 | kubectl_command: config[:kubectl_command], 43 | rsync_command: config[:rsync_command], 44 | rsync_rsh: config[:rsync_rsh], 45 | log_level: config[:log_level], 46 | logger: logger 47 | ).tap do |conn| 48 | block.call(conn) if block 49 | end 50 | end 51 | 52 | class Connection < Kitchen::Transport::Base::Connection 53 | include ShellOut 54 | include KitchenKubernetes::Helper 55 | 56 | # (see Base::Connection#execute) 57 | def execute(command) 58 | return if command.nil? 59 | # Run via kubectl exec. 60 | run_command(kubectl_command('exec', '--tty', '--container=default', options[:pod_id], '--', *Shellwords.split(command))) 61 | end 62 | 63 | # (see Base::Connection#upload) 64 | def upload(locals, remote) 65 | return if locals.empty? 66 | # Use rsync over kubectl exec to send files. 67 | run_command([options[:rsync_command], '--archive', '--progress', '--blocking-io', '--rsh', options[:rsync_rsh]] + (options[:log_level] == :debug ? %w{--verbose --verbose --verbose} : []) + locals + ["#{options[:pod_id]}:#{remote}"]) 68 | end 69 | 70 | # (see Base::Connection#login_command) 71 | def login_command 72 | # Find a valid login shell and exec it. This is so weridly complex 73 | # because it has to work with a /bin/sh that might be bash, dash, or 74 | # busybox. Also CentOS images doesn't have `which` for some reason. 75 | # Dash's `type` is super weird so use `which` first in case of dash but 76 | # fall back to `type` for basically just CentOS. 77 | login_cmd = "IFS=$'\n'; for f in `which bash zsh sh 2>/dev/null || type -P bash zsh sh`; do exec \"$f\" -l; done" 78 | cmd = kubectl_command('exec', '--stdin', '--tty', '--container=default', options[:pod_id], '--', '/bin/sh', '-c', login_cmd) 79 | LoginCommand.new(cmd[0], cmd.drop(1)) 80 | end 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kitchen-Kubernetes 2 | 3 | [![Build Status](https://img.shields.io/travis/coderanger/kitchen-kubernetes.svg)](https://travis-ci.org/coderanger/kitchen-kubernetes) 4 | [![Gem Version](https://img.shields.io/gem/v/kitchen-kubernetes.svg)](https://rubygems.org/gems/kitchen-kubernetes) 5 | [![Gemnasium](https://img.shields.io/gemnasium/coderanger/kitchen-kubernetes.svg)](https://gemnasium.com/coderanger/kitchen-kubernetes) 6 | [![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) 7 | 8 | A [Test Kitchen](https://kitchen.ci/) driver for testing on top of a Kubernetes 9 | cluster. It is currently aimed at Chef cookbook testing, though see the [FAQ](#FAQ) 10 | for more information on other testing targets. 11 | 12 | ## Quick Start 13 | 14 | First install the driver: 15 | 16 | ```bash 17 | chef gem install kitchen-kubernetes 18 | ``` 19 | 20 | Then configure your `.kitchen.yml` to use the driver: 21 | 22 | ```yaml 23 | driver: 24 | name: kubernetes 25 | ``` 26 | 27 | No other options should be required for the default case but [see below](#options) 28 | for more information on available configuration options. 29 | 30 | ## Options 31 | 32 | You can customize things by setting additional options in the `driver` section, 33 | for example: 34 | 35 | ```yaml 36 | driver: 37 | name: kubernetes 38 | kubectl_command: ~/bin/kubectl 39 | chef_version: 13.5.21 40 | ``` 41 | 42 | * `cache_path` - Host path for the Chef installation cache. See [below](#chef-install). *(default: /data/chef/%{chef_version})* 43 | * `cache_volume` - Kubernetes [volume](https://kubernetes.io/docs/api-reference/v1.8/#volume-v1-core) description for the Chef install volume. *(default: auto-generated)* 44 | * `chef_image` - Docker data image to get the Chef installation from. *(default: chef/chef)* 45 | * `chef_version` - Version of the Chef data image to use. *(default: latest)* 46 | * `context` - Kubectl configuration context to use. *(default: current-context)* 47 | * `image` - Docker image for the main container in the pod, where Chef is run. *(default: based on the platform name and version)* 48 | * `init_system` - Init system to activate in the default container. See #systemd. *(default: nil)* 49 | * `kubectl_command` - Path to the `kubectl` command to use. *(default: kubectl)* 50 | * `pod_name` - Name of the generated pod. *(default: auto-generate)* 51 | * `pod_template` - Path to the Erb template to create the pod. See [below](#pod). *(default: internal)* 52 | * `rsync_command` - Path to the `rsync` command to use. *(default: rsync)* 53 | * `rsync_image` - Docker image to use for the rsync container in the pod. *(default: kitchenkubernetes/rsync)* 54 | 55 | ## Chef Install 56 | 57 | In a similar fashion to [`kitchen-dokken`](https://github.com/someara/kitchen-dokken/), 58 | this driver uses the `chef/chef` Docker Hub images instead of the normal Chef 59 | installers. Unfortunately Kubernetes doesn't support using Docker images as 60 | volumes directly, so there has to be some copying from the data container in to a 61 | volume shared between the data container and main container. 62 | 63 | So as to reduce the disk I/O from launching our test pods (a Chef install is 64 | about 50MB so this is ~100MB of file I/O each time) we use a hostPath volume to 65 | give the cache some persistence between test runs. You can control the location 66 | of this hostPath volume using the `cache_path` configuration option: 67 | 68 | ```yaml 69 | driver: 70 | name: kubernetes 71 | cache_path: /home/k8s/chef_cache/%{chef_version} 72 | ``` 73 | 74 | You can also unset `cache_path` to totally disable the cache and use an 75 | `emptyDir` volume instead: 76 | 77 | ```yaml 78 | driver: 79 | name: kubernetes 80 | cache_path: null 81 | ``` 82 | 83 | If you have another shared storage option available, you can also set 84 | `cache_volume` to a Kuberenetes [Volume object]((https://kubernetes.io/docs/api-reference/v1.8/#volume-v1-core)) 85 | (minus the `name` field) and that will be used instead: 86 | 87 | ```yaml 88 | driver: 89 | name: kubernetes 90 | cache_volume: 91 | persistentVolumeClaim: 92 | claimName: myclaim 93 | ``` 94 | 95 | ## Pod 96 | 97 | The default pod created for testing involves three containers and two volumes. 98 | 99 | The `chef` volume is [discussed above](#chef-install) and contains the `/opt/chef` 100 | folder of the Chef installation. The `kitchen` volume is an `emptyDir` used for 101 | sending files in to the test container. 102 | 103 | The `chef` container is an initContainer (meaning it runs before the other two) 104 | which handles copying the Chef install from the data container to the `chef` 105 | volume (with some caching by default so most of the time it does nothing, as mentioned 106 | above). The `rsync` container runs a small image containing nothing but Rsync, 107 | used [as part of the file upload system](#transport). The `default` container is 108 | where Chef is run, as well as the eventual tests. 109 | 110 | If overriding the `pod_template` configuration option, make sure your pod template 111 | also includes containers named `default` and `rsync`. 112 | 113 | ## Transport 114 | 115 | Careful observers will note that their `kitchen list` output shows that not just 116 | the driver is set to `Kubernetes`, but the transport is too: 117 | 118 | ```bash 119 | $ kitchen list 120 | Instance Driver Provisioner Verifier Transport Last Action Last Error 121 | default-centos-7 Kubernetes ChefSolo Busser Kubernetes 122 | ``` 123 | 124 | This transport plugin is configured automatically by the driver for your convenience 125 | and uses `kubectl exec` for running commands on the test container. For sending 126 | files, rsync is used. The rsync data is also sent over `kubectl exec` to avoid 127 | needing to configure any additional network login services. This uses the `rsync` 128 | container in the pod and the `kitchen` volume. This does require `rsync` is also 129 | installed on your workstation (or where ever `kitchen` is running from). 130 | 131 | ## Systemd 132 | 133 | If you want to enable systemd inside the container, set `init_system: systemd`. 134 | For example: 135 | 136 | ```yaml 137 | driver: 138 | name: kubernetes 139 | 140 | platforms: 141 | - name: centos-7 142 | driver: 143 | init_system: systemd 144 | - name: ubuntu-16.04 145 | driver: 146 | init_system: systemd 147 | ``` 148 | 149 | As a word of warning, the default `debian` Docker images do not include systemd, 150 | so you will need to make your own that have it installed if you want to test on 151 | systemd and Debian. 152 | 153 | ## FAQ 154 | 155 | ### Can I use this for testing things other than Chef? 156 | 157 | Not currently, but it could definitely be added. If you're interested in a 158 | particular provisioner, open an issue so I know there is user demand. 159 | 160 | ### Can I test Kubernetes objects like deployments and services? 161 | 162 | Not yet, though it is planned. If you have a specific use case in mind, please 163 | open an issue and let me know. 164 | 165 | ### Will this work with GKE/AKE/etc? 166 | 167 | I think so but haven't tested it myself. 168 | 169 | ### Will this work with minkube? 170 | 171 | Yes, just make sure you have the minikube config context activated. 172 | 173 | ## Sponsors 174 | 175 | Development sponsored by [SAP](https://www.sap.com/). 176 | 177 | ## License 178 | 179 | Copyright 2017, Noah Kantrowitz 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /lib/kitchen/driver/kubernetes.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017, Noah Kantrowitz 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | require 'erb' 18 | require 'json' 19 | 20 | require 'kitchen/driver/base' 21 | require 'kitchen/provisioner/chef_base' 22 | require 'kitchen/shell_out' 23 | require 'kitchen/verifier/busser' 24 | 25 | require 'kitchen/transport/kubernetes' 26 | require 'kitchen-kubernetes/helper' 27 | 28 | 29 | module Kitchen 30 | module Driver 31 | 32 | # Kubernetes driver for Kitchen. 33 | # 34 | # @author Noah Kantrowitz 35 | # @since 1.0 36 | # @see Kitchen::Transport::Kubernetes 37 | class Kubernetes < Kitchen::Driver::Base 38 | include ShellOut 39 | include KitchenKubernetes::Helper 40 | 41 | default_config :cache_path, '/data/chef/%{chef_version}' 42 | default_config :chef_image, 'chef/chef' 43 | default_config :chef_version, 'latest' 44 | default_config :context, nil 45 | default_config :image_pull_policy, nil 46 | default_config :image_pull_secrets, nil 47 | default_config :init_system, nil 48 | default_config :kubectl_command, 'kubectl' 49 | default_config :pod_template, File.expand_path('../pod.yaml.erb', __FILE__) 50 | default_config :rsync_command, 'rsync' 51 | default_config :rsync_image, 'kitchenkubernetes/rsync:3.1.2-r5' 52 | default_config :rsync_rsh, "#{RbConfig.ruby} -e \"exec('kubectl', 'exec', '--stdin', '--container=rsync', ARGV[0], '--', *ARGV[1..-1])\"" 53 | 54 | default_config :cache_volume do |driver| 55 | if driver[:cache_path] 56 | path = driver[:cache_path] % {chef_version: driver[:chef_version]} 57 | {hostPath: {path: path, type: 'DirectoryOrCreate'}} 58 | else 59 | {emptyDir: {}} 60 | end 61 | end 62 | 63 | default_config :image do |driver| 64 | driver.default_image 65 | end 66 | 67 | default_config :pod_name do |driver| 68 | # Borrowed from kitchen-rackspace. 69 | [ 70 | driver.instance.name.gsub(/\W/, ''), 71 | (Etc.getlogin || 'nologin').gsub(/\W/, ''), 72 | Socket.gethostname.gsub(/\W/, '')[0..20], 73 | Array.new(8) { rand(36).to_s(36) }.join 74 | ].join('-').downcase 75 | end 76 | 77 | # Don't expand path on commands that don't look like a path, otherwise 78 | # it turns kubectl in to /path/to/cookbook/kubectl. 79 | expand_path_for :kubectl_command do |driver| 80 | driver[:kubectl_command] =~ %r{/|\\} 81 | end 82 | expand_path_for :pod_template 83 | expand_path_for :rsync_command do |driver| 84 | driver[:rsync_command] =~ %r{/|\\} 85 | end 86 | 87 | # Work out the default primary container image to use for this instance. 88 | # Can be overridden by subclasses. Must return a string compatible with 89 | # a Kubernetes container image specification. 90 | # 91 | # @return [String] 92 | def default_image 93 | if instance.platform.name =~ /^(.*)-([^-]*)$/ 94 | "#{$1}:#{$2}" 95 | else 96 | instance.platform.name 97 | end 98 | end 99 | 100 | # Muck with some other plugins to make the UX easier. Haxxxx. 101 | # 102 | # @api private 103 | def finalize_config!(instance) 104 | super.tap do 105 | # Force the use of the Kubernetes transport since it isn't much use 106 | # without that. 107 | instance.transport = Kitchen::Transport::Kubernetes.new(config) 108 | # Leave room for the possibility of other provisioners in the future, 109 | # but force some options we need. 110 | if instance.provisioner.is_a?(Kitchen::Provisioner::ChefBase) 111 | instance.provisioner.send(:config).update( 112 | require_chef_omnibus: false, 113 | product_name: nil, 114 | chef_omnibus_root: '/opt/chef', 115 | sudo: false, 116 | ) 117 | end 118 | # Ditto to the above, other verifiers will need their own hacks, but 119 | # this is a start at least. 120 | if instance.verifier.is_a?(Kitchen::Verifier::Busser) 121 | instance.verifier.send(:config).update( 122 | root_path: '/tmp/kitchen/verifier', 123 | sudo: false, 124 | ) 125 | elsif defined?(Kitchen::Verifier::Inspec) && instance.verifier.is_a?(Kitchen::Verifier::Inspec) 126 | # Monkeypatch kitchen-inspec to use my copy of the kubernetes train transport. 127 | # Pending https://github.com/chef/train/pull/205 and https://github.com/chef/kitchen-inspec/pull/148 128 | # or https://github.com/chef/kitchen-inspec/pull/149. 129 | require 'kitchen/verifier/train_kubernetes_hack' 130 | _config = config # Because closure madness. 131 | old_runner_options = instance.verifier.method(:runner_options) 132 | instance.verifier.send(:define_singleton_method, :runner_options) do |transport, state = {}, platform = nil, suite = nil| 133 | if transport.is_a?(Kitchen::Transport::Kubernetes) 134 | # Initiate 1337 ha><0rz. 135 | { 136 | "backend" => "kubernetes_hack", 137 | "logger" => logger, 138 | "pod" => state[:pod_id], 139 | "container" => "default", 140 | "kubectl_path" => _config[:kubectl_path], 141 | "context" => _config[:context], 142 | }.tap do |runner_options| 143 | # Copied directly from kitchen-inspec because there is no way not to. Sigh. 144 | runner_options["color"] = (config[:color].nil? ? true : config[:color]) 145 | runner_options["format"] = config[:format] unless config[:format].nil? 146 | runner_options["output"] = config[:output] % { platform: platform, suite: suite } unless config[:output].nil? 147 | runner_options["profiles_path"] = config[:profiles_path] unless config[:profiles_path].nil? 148 | runner_options[:controls] = config[:controls] 149 | end 150 | else 151 | old_runner_options.call(transport, state, platform, suite) 152 | end 153 | end 154 | end 155 | end 156 | end 157 | 158 | # (see Base#create) 159 | def create(state) 160 | # Already created, we're good. 161 | return if state[:pod_id] 162 | # Lock in our name with randomness and whatever. 163 | pod_id = config[:pod_name] 164 | # Render the pod YAML and feed it to kubectl. 165 | tpl = ERB.new(IO.read(config[:pod_template]), 0, '-') 166 | tpl.filename = config[:pod_template] 167 | pod_yaml = tpl.result(binding) 168 | debug("Creating pod with YAML:\n#{pod_yaml}\n") 169 | run_command(kubectl_command('create', '--filename', '-'), input: pod_yaml) 170 | # Wait until the pod reaches Running status. 171 | status = nil 172 | start_time = Time.now 173 | while status != 'Running' 174 | if Time.now - start_time > 20 175 | # More than 20 seconds, start giving user feedback. 20 second threshold 176 | # was 100% pulled from my ass based on how long it takes to launch 177 | # on my local minikube, may need changing for reality. 178 | info("Waiting for pod #{pod_id} to be running, currently #{status}") 179 | end 180 | sleep(1) 181 | # Can't use run_command here because error! is unwanted and logging is a bit much. 182 | status_cmd = Mixlib::ShellOut.new(kubectl_command('get', 'pod', pod_id, '--output=json')) 183 | status_cmd.run_command 184 | unless status_cmd.error? || status_cmd.stdout.empty? 185 | status = JSON.parse(status_cmd.stdout)['status']['phase'] 186 | end 187 | end 188 | # Save the pod ID. 189 | state[:pod_id] = pod_id 190 | rescue Exception => ex 191 | # If something goes wrong, try to clean up. 192 | if pod_id 193 | begin 194 | debug("Failure during create, trying to clean up pod #{pod_id}") 195 | run_command(kubectl_command('delete', 'pod', pod_id, '--now')) 196 | rescue ShellCommandFailed => cleanup_ex 197 | # Welp, we tried. 198 | debug("Cleanup failed, continuing anyway: #{cleanup_ex}") 199 | end 200 | end 201 | raise ex 202 | end 203 | 204 | # (see Base#destroy) 205 | def destroy(state) 206 | return unless state[:pod_id] 207 | run_command(kubectl_command('delete', 'pod', state[:pod_id], '--now')) 208 | # Explicitly not waiting for the delete to finish, if k8s has problems 209 | # with deletes in the future, I can add a wait here. 210 | rescue ShellCommandFailed => ex 211 | raise unless ex.to_s.include?('(NotFound)') 212 | end 213 | 214 | end 215 | end 216 | end 217 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------