kitchen-kubernetes 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,84 @@
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 <noah@coderanger>
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
@@ -0,0 +1,90 @@
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
data/rsync/Dockerfile ADDED
@@ -0,0 +1,21 @@
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"]
data/test/.kitchen.yml ADDED
@@ -0,0 +1,57 @@
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
@@ -0,0 +1,17 @@
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'
@@ -0,0 +1,30 @@
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
@@ -0,0 +1,28 @@
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'
@@ -0,0 +1 @@
1
+ ver=<%= @chef_version %>
@@ -0,0 +1,22 @@
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'
@@ -0,0 +1,19 @@
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'
@@ -0,0 +1,19 @@
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'
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,29 @@
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