kitchen-kubernetes 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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