train-kubernetes-docs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1193da3e4b88cdff83a82226a1a842cefa49b690
4
+ data.tar.gz: c359a0ff97031ffb4b27d5d8345cb8c27063be02
5
+ SHA512:
6
+ metadata.gz: feac8aa75a0d79b1e6f1e29ee426acf7d8c0b8b8c35832275d496801b09450dc234a9c7c527720fbbf0cc9b6e16ace526889abec644145861bca2ae16c09f3a6
7
+ data.tar.gz: f10a3f01aeaf43f59023d0b46fb1ade9f48bc385e0c134745760752f2f043be24d60f43ceb0d600fd47ab406c4e77ec782badc84b6671008861cdfe31d342e49
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ # Remaining group is only used for development.
6
+ group :development do
7
+ gem 'bundler'
8
+ gem 'byebug'
9
+ gem 'inspec', '>= 3.7.11' # We need InSpec for the test harness while developing.
10
+ gem 'minitest'
11
+ gem 'rake'
12
+ gem 'rubocop', '~> 0.59'
13
+ end
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # Kubernetes transport plugin for Chef Inspec Train
2
+
3
+ This plugin allows applications that rely on Train to communicate with the Kubernetes API. For example, InSpec uses this to perform compliance checks against any resource in the Kubernetes API. Train plugins are managed by InSpec CLI.
4
+
5
+ ## Usage
6
+
7
+ When used in combination with the [InSpec Kubernetes Resource Pack](https://github.com/inspec/inspec-k8s) you can validate the spec of any Kubernetes resource you have access to:
8
+
9
+ ```ruby
10
+ describe k8sobjects(api: 'v1', type: 'pods', namespace: 'default', labelSelector: 'run=nginx') do
11
+ it { should exist }
12
+ ...
13
+ end
14
+ ```
15
+
16
+ ```ruby
17
+ describe k8sobjects(api: 'v1', type: 'namespaces', labelSelector: 'myns=prod') do
18
+ it { should exist }
19
+ ...
20
+ end
21
+ ```
22
+
23
+ ```ruby
24
+ describe k8sobject(api: 'v1', type: 'pod', namespace: 'default', name: 'my-pod') do
25
+ it { should exist }
26
+ its('name') { should eq 'my-pod' }
27
+ ...
28
+ end
29
+ ```
30
+
31
+ ## File resource
32
+ ```ruby
33
+ inspec.backend.file('PATH', pod: 'POD', container: 'CONTAINER', namespace: 'NAMESPACE')
34
+ ```
35
+ Currently it supports only Linux based containers
36
+
37
+
38
+ ## Preconditions
39
+
40
+ - InSpec 3.7+ or 4.x+
41
+ - Ruby 2.4+
42
+ - You have set the env var KUBECONFIG or have a valid ~/.kube/config
43
+
44
+ ## Installation
45
+
46
+ **_NOTE:_** **_The train-kubernetes-docs plugin now comes included with InSpec 5.22.0 and later, and does not require installation separately._**
47
+
48
+ Train plugins are distributed as gems. You may choose to manage the gem yourself, but if you are an InSpec user, InSPec can handle it for you.
49
+
50
+ First, run:
51
+
52
+ ```
53
+ gem install train-kubernetes-docs
54
+ ```
55
+
56
+ Next, run:
57
+
58
+ ```
59
+ inspec plugin install train-kubernetes-docs
60
+ ```
61
+
62
+ Next, view the `plugins.json`:
63
+
64
+ ```
65
+ vi ~/.inspec/plugins.json
66
+ ```
67
+
68
+ If it has the version set to `"= 0.1.3"`, modify it to `"0.1.3"` and save the file.
69
+
70
+ Verify the plugin:
71
+
72
+ ```
73
+ inspec plugin list
74
+ ```
75
+
76
+ ```
77
+ inspec detect -t k8s://
78
+
79
+ == Platform Details
80
+
81
+ Name: k8s
82
+ Families: cloud, api
83
+ Release: 0.1.3
84
+ ```
85
+
86
+ ## Troubleshooting (Only applicable for InSpec version 5.21.* and earlier)
87
+
88
+ If you run into issues installing via `inspec plugin install train-kubernetes-docs`, try:
89
+
90
+ * Try running `gem install train-kubernetes-docs` before `inspec plugin install train-kubernetes-docs`.
91
+ * Ensure the `~/.inspec/plugins.json` has `"0.1.3"` and not `"= 0.1.3"` for the `version` value. Modify it by hand if needed.
92
+ * Ensure you can cleanly install the `k8s-ruby` gem version `0.10.4` or greater. e.g. `gem install k8s-ruby -v 0.10.4`
93
+ * Ensure that only one version of the `excon` gem is installed. e.g. `gem list | grep excon`. If you see two versions, `gem uninstall excon` and remove the older version.
94
+
95
+ ## Reporting Issues
96
+
97
+ Bugs, typos, limitations, and frustrations are welcome to be reported through the [GitHub issues page for the train-kubernetes-docs project](https://github.com/inspec/train-kubernetes-docs/issues).
98
+
99
+ You may also ask questions in the #inspec channel of the Chef Community Slack team. However, for an issue to get traction, please report it as a github issue.
100
+
101
+ ### Development Process
102
+
103
+ If you wish to contribute to this plugin, please use the usual fork-branch-push-PR cycle. All functional changes need new tests, and bugfixes are expected to include a new test that demonstrates the bug.
104
+
105
+ ### Reference Information
106
+
107
+ [Plugin Development](https://github.com/inspec/train/blob/master/docs/dev/plugins.md) is documented on the `train` project on GitHub.
@@ -0,0 +1,58 @@
1
+ require 'train'
2
+ require 'k8s-ruby'
3
+ require 'train-kubernetes-docs/platform'
4
+ require 'train-kubernetes-docs/kubectl_client'
5
+
6
+ module TrainPlugins
7
+ module TrainKubernetes
8
+ class Connection < Train::Plugins::Transport::BaseConnection
9
+ include TrainPlugins::TrainKubernetes::Platform
10
+
11
+ def initialize(options)
12
+ super(options)
13
+ @pod = options[:pod] || options[:path]&.gsub('/', '')
14
+ @container = options[:container]
15
+ @namespace = options[:namespace] || options[:host]
16
+ parse_kubeconfig
17
+ connect
18
+ end
19
+
20
+ attr_accessor :client
21
+
22
+ def connect
23
+ @client.apis(prefetch_resources: true)
24
+ rescue Excon::Error::Socket => e
25
+ logger.error e.message
26
+ exit
27
+ end
28
+
29
+ def uri
30
+ "kubernetes://#{unique_identifier}"
31
+ end
32
+
33
+ def unique_identifier
34
+ @client.transport.server.gsub(%r{(http|https)\:\/\/}, '') || 'default'
35
+ end
36
+
37
+ def parse_kubeconfig
38
+ kubeconfig_file = @options[:kubeconfig] if @options[:kubeconfig]
39
+ @client = K8s::Client.config(K8s::Config.load_file(::File.expand_path(kubeconfig_file)))
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :pod, :container, :namespace
45
+
46
+ def run_command_via_connection(cmd, opts = {}, &_data_handler)
47
+ KubectlClient.new(pod: opts[:pod] || pod,
48
+ container: opts[:container] || container,
49
+ namespace: opts[:namespace] || namespace)
50
+ .execute(cmd)
51
+ end
52
+
53
+ def file_via_connection(path, **args)
54
+ TrainPlugins::TrainKubernetes::File::Linux.new(self, path, pod: args[:pod], container: args[:container], namespace: args[:namespace])
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,149 @@
1
+ require 'train/file/remote/linux'
2
+ require 'train/extras/stat'
3
+
4
+ module TrainPlugins
5
+ module TrainKubernetes
6
+ module File
7
+ class Linux < ::Train::File::Remote::Linux
8
+ def initialize(backend, path, follow_symlink = true, pod:, **args)
9
+ @backend = backend
10
+ @path = path || ''
11
+ @follow_symlink = follow_symlink
12
+ @pod = pod
13
+ @container = args[:container]
14
+ @namespace = args[:namespace]
15
+
16
+ sanitize_filename(path)
17
+ super(backend, path, follow_symlink)
18
+ end
19
+
20
+ def content
21
+ return @content if defined?(@content)
22
+
23
+ @content = @backend.run_command("cat #{@spath} || echo -n",
24
+ { pod: pod, namespace: namespace, container: container })
25
+ .stdout
26
+ return @content unless @content.empty?
27
+
28
+ @content = nil if directory? || size.nil? || (size > 0)
29
+ @content
30
+ end
31
+
32
+ def content=(new_content)
33
+ execute_result = @backend.run_command('base64 --help', { pod: pod, namespace: namespace, container: container })
34
+ if execute_result.exit_status != 0
35
+ raise TransportError, "#{self.class} found no base64 binary for file writes"
36
+ end
37
+
38
+ unix_cmd = format("echo '%<base64>s' | base64 --decode > %<file>s",
39
+ base64: Base64.strict_encode64(new_content),
40
+ file: @spath)
41
+
42
+ @backend.run_command(unix_cmd, { pod: pod, namespace: namespace, container: container })
43
+
44
+ @content = new_content
45
+ end
46
+
47
+ def exist?
48
+ @exist ||= begin
49
+ f = @follow_symlink ? '' : " || test -L #{@spath}"
50
+ @backend.run_command("test -e #{@spath}" + f,
51
+ { pod: pod, namespace: namespace, container: container })
52
+ .exit_status == 0
53
+ end
54
+ end
55
+
56
+ def mounted
57
+ @mounted ||=
58
+ @backend.run_command("mount | grep -- ' on #{@path} '",
59
+ { pod: pod, namespace: namespace, container: container })
60
+ end
61
+
62
+ def path
63
+ return @path unless @follow_symlink && symlink?
64
+
65
+ @link_path ||= read_target_path
66
+ end
67
+
68
+ def shallow_link_path
69
+ return nil unless symlink?
70
+
71
+ @shallow_link_path ||=
72
+ @backend.run_command("readlink #{@spath}", { pod: pod, namespace: namespace, container: container })
73
+ .stdout
74
+ .chomp
75
+ end
76
+
77
+ def stat
78
+ @stat ||= begin
79
+ shell_escaped_path = @spath
80
+ backend = @backend
81
+ follow_symlink = @follow_symlink
82
+ lstat = follow_symlink ? ' -L' : ''
83
+ format = '--printf'
84
+ res = backend.run_command("stat#{lstat} #{shell_escaped_path} 2>/dev/null #{format} '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'",
85
+ { pod: pod, namespace: namespace, container: container })
86
+ # ignore the exit_code: it is != 0 if selinux labels are not supported
87
+ # on the system.
88
+
89
+ fields = res.stdout.split("\n")
90
+ return {} if fields.length != 9
91
+
92
+ tmask = fields[1].to_i(16)
93
+ selinux = fields[8]
94
+ ## selinux security context string not available on esxi
95
+ selinux = nil if (selinux == '?') || (selinux == '(null)') || (selinux == 'C')
96
+ {
97
+ type: Train::Extras::Stat.find_type(tmask),
98
+ mode: tmask & 07777,
99
+ owner: fields[2],
100
+ uid: fields[3].to_i,
101
+ group: fields[4],
102
+ gid: fields[5].to_i,
103
+ mtime: fields[7].to_i,
104
+ size: fields[0].to_i,
105
+ selinux_label: selinux
106
+ }
107
+ end
108
+ end
109
+
110
+ def source
111
+ if @follow_symlink
112
+ self.class.new(@backend, @path, false, pod: pod, container: container, namespace: namespace)
113
+ else
114
+ self
115
+ end
116
+ end
117
+
118
+ def user_permissions
119
+ return {} unless exist?
120
+
121
+ skip_reource '`user_permissions` is not supported on your Linux Containers yet.'
122
+ end
123
+
124
+ def inherited?
125
+ return false unless exist?
126
+
127
+ skip_resource '`inherited?` is not supported on your Linux Containers yet.'
128
+ end
129
+
130
+ private
131
+
132
+ # Returns full path of a symlink target(real dest) or '' on symlink loop
133
+ def read_target_path
134
+ full_path = @backend.run_command("readlink -n #{@spath} -f",
135
+ { pod: pod, namespace: namespace, container: container })
136
+ .stdout
137
+ # Needed for some OSes like OSX that returns relative path
138
+ # when the link and target are in the same directory
139
+ if !full_path.start_with?('/') && full_path != ''
140
+ full_path = ::File.expand_path("../#{full_path}", @spath)
141
+ end
142
+ full_path
143
+ end
144
+
145
+ attr_reader :pod, :container, :namespace
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,50 @@
1
+ require 'inspec/resources/file'
2
+ require 'inspec/exceptions'
3
+
4
+ module TrainPlugins
5
+ module TrainKubernetes
6
+ module File
7
+ class LinuxImmutableFileCheck < Inspec::Resources::LinuxImmutableFlagCheck
8
+ def initialize(inspec, file, pod:, container: nil, namespace: nil)
9
+ @pod = pod
10
+ @container = container
11
+ @namespace = namespace
12
+ super(inspec, file)
13
+ end
14
+
15
+ def find_utility_or_error(utility_name)
16
+ %W(/usr/sbin/#{utility_name} /sbin/#{utility_name} /usr/bin/#{utility_name} /bin/#{utility_name} #{utility_name}).each do |cmd|
17
+ if inspec.backend
18
+ .run_command("sh -c 'type \"#{cmd}\"'", { pod: pod, container: container, namespace: namespace })
19
+ .exit_status.to_i == 0
20
+ return cmd
21
+ end
22
+ end
23
+
24
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `#{utility_name}`"
25
+ end
26
+
27
+ def is_immutable?
28
+ # Check if lsattr is available. In general, all linux system has lsattr & chattr
29
+ # This logic check is valid for immutable flag set with chattr
30
+ utility = find_utility_or_error('lsattr')
31
+
32
+ utility_cmd = inspec.backend.run_command("#{utility} #{file_path}",
33
+ { pod: pod, container: container, namespace: namespace })
34
+
35
+ raise Inspec::Exceptions::ResourceFailed, "Executing #{utility} #{file_path} failed: #{utility_cmd.stderr}" if utility_cmd.exit_status.to_i != 0
36
+
37
+ # General output for lsattr file_name is:
38
+ # ----i---------e----- file_name
39
+ # The fifth char resembles the immutable flag. Total 20 flags are allowed.
40
+ lsattr_info = utility_cmd.stdout.strip.squeeze(' ')
41
+ lsattr_info =~ /^.{4}i.{15} .*/
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :pod, :container, :namespace
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ module TrainPlugins
2
+ module TrainKubernetes
3
+ module File
4
+ class LinuxPermissions < Inspec::Resources::UnixFilePermissions
5
+ def initialize(inspec, pod:, namespace: nil, container: nil)
6
+ @pod = pod
7
+ @namespace = namespace
8
+ @container = container
9
+ super(inspec)
10
+ end
11
+
12
+ def check_file_permission_by_user(access_type, user, path)
13
+ flag = permission_flag(access_type)
14
+ perm_cmd = "su -s /bin/sh -c \"test -#{flag} #{path}\" #{user}"
15
+ cmd = inspec.backend.run_command(perm_cmd, { pod: pod, namespace: namespace, container: container })
16
+ cmd.exit_status == 0
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :pod, :container, :namespace
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ require 'mixlib/shellout'
2
+ require 'train/extras'
3
+
4
+ module TrainPlugins
5
+ module TrainKubernetes
6
+ class KubectlClient
7
+ attr_reader :pod, :container, :namespace
8
+
9
+ DEFAULT_NAMESPACE = 'default'.freeze
10
+
11
+ def initialize(pod:, namespace: nil, container: nil)
12
+ @pod = pod
13
+ @container = container
14
+ @namespace = namespace || DEFAULT_NAMESPACE
15
+ end
16
+
17
+ def execute(command, stdin: true, tty: true)
18
+ instruction = build_instruction(command, stdin, tty)
19
+ shell = Mixlib::ShellOut.new(instruction)
20
+ res = shell.run_command
21
+ Train::Extras::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
22
+ rescue Errno::ENOENT => _e
23
+ Train::Extras::CommandResult.new('', '', 1)
24
+ end
25
+
26
+ private
27
+
28
+ def shell
29
+ @shell ||= Mixlib::ShellOut.new(instruction)
30
+ end
31
+
32
+ def build_instruction(command, stdin, _tty)
33
+ ['kubectl exec'].tap do |arr|
34
+ arr << '--stdin' if stdin
35
+ arr << pod if pod
36
+ arr << '-n'
37
+ arr << namespace
38
+ arr << '--'
39
+ arr << command
40
+ end.join("\s")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ module TrainPlugins
2
+ module TrainKubernetes
3
+ module Platform
4
+ def platform
5
+ Train::Platforms.name('k8s').in_family('cloud')
6
+ force_platform!('k8s', release: TrainPlugins::TrainKubernetes::VERSION)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ require 'train-kubernetes-docs/connection'
2
+
3
+ module TrainPlugins
4
+ module TrainKubernetes
5
+ class Transport < Train.plugin(1)
6
+ name 'k8s'
7
+ option :kubeconfig, default: ENV['KUBECONFIG'] || '~/.kube/config'
8
+ option :pod, default: nil
9
+ option :container, default: nil
10
+ option :namespace, default: nil
11
+ def connection(_instance_opts = nil)
12
+ @connection ||= TrainPlugins::TrainKubernetes::Connection.new(@options)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ # This file exists simply to record the version number of the plugin.
2
+ # It is kept in a separate file, so that your gemspec can load it and
3
+ # learn the current version without loading the whole plugin. Also,
4
+ # many CI servers can update this file when "version bumping".
5
+
6
+ module TrainPlugins
7
+ module TrainKubernetes
8
+ VERSION = '0.1.0'.freeze
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ # This file is known as the "entry point."
2
+ # This is the file Train will try to load if it
3
+ # thinks your plugin is needed.
4
+
5
+ # The *only* thing this file should do is setup the
6
+ # load path, then load plugin files.
7
+
8
+ # Next two lines simply add the path of the gem to the load path.
9
+ # This is not needed when being loaded as a gem; but when doing
10
+ # plugin development, you may need it. Either way, it's harmless.
11
+ libdir = File.dirname(__FILE__)
12
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
13
+
14
+ # It's traditional to keep your gem version in a separate file, so CI can find it easier.
15
+ require 'train-kubernetes-docs/version'
16
+
17
+ # A train plugin has three components: Transport, Connection, and Platform.
18
+ # Transport acts as the glue.
19
+ require 'train-kubernetes-docs/transport'
20
+ require 'train-kubernetes-docs/platform'
21
+ require 'train-kubernetes-docs/file/linux'
@@ -0,0 +1,50 @@
1
+ # As plugins are usually packaged and distributed as a RubyGem,
2
+ # we have to provide a .gemspec file, which controls the gembuild
3
+ # and publish process. This is a fairly generic gemspec.
4
+
5
+ # It is traditional in a gemspec to dynamically load the current version
6
+ # from a file in the source tree. The next three lines make that happen.
7
+ # lib = File.expand_path('../lib', __FILE__)
8
+ lib = File.expand_path('lib', __dir__)
9
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
10
+ require 'train-kubernetes-docs/version'
11
+
12
+ Gem::Specification.new do |spec|
13
+ # Importantly, all Train plugins must be prefixed with `train-`
14
+ spec.name = 'train-kubernetes-docs'
15
+
16
+ # It is polite to namespace your plugin under TrainPlugins::YourPluginInCamelCase
17
+ spec.version = TrainPlugins::TrainKubernetes::VERSION
18
+ spec.authors = ['Brad Geesaman']
19
+ spec.email = ['bradgeesaman@gmail.com']
20
+ spec.summary = 'DOCS Train Kubernetes'
21
+ spec.description = 'A Train "transport" plugin for Chef Inspec that allows testing of all Kubernetes API resources'
22
+ spec.homepage = 'https://github.com/inspec/train-kubernetes-docs'
23
+ spec.license = 'Apache-2.0'
24
+
25
+ spec.required_ruby_version = '>= 2.4'
26
+
27
+ # Though complicated-looking, this is pretty standard for a gemspec.
28
+ # It just filters what will actually be packaged in the gem (leaving
29
+ # out tests, etc)
30
+ spec.files = %w{
31
+ README.md train-kubernetes-docs.gemspec Gemfile
32
+ } + Dir.glob(
33
+ 'lib/**/*', File::FNM_DOTMATCH
34
+ ).reject { |f| File.directory?(f) }
35
+ spec.require_paths = ['lib']
36
+
37
+ # If you rely on any other gems, list them here with any constraints.
38
+ # This is how `inspec plugin install` is able to manage your dependencies.
39
+ # For example, perhaps you are writing a thing that talks to AWS, and you
40
+ # want to ensure you have `aws-sdk` in a certain version.
41
+
42
+ # If you only need certain gems during development or testing, list
43
+ # them in Gemfile, not here.
44
+ # Do not list inspec as a dependency of the train plugin.
45
+
46
+ # All plugins should mention train, > 1.4
47
+ # pinning k8s-ruby to 0.10.5 to avoid broken dry-type gem upgrades from k8s-ruby
48
+ spec.add_dependency 'k8s-ruby', '~> 0.14.0'
49
+ spec.add_dependency 'train', '~> 3.0'
50
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: train-kubernetes-docs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Brad Geesaman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-10-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: k8s-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.14.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.14.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: train
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ description: A Train "transport" plugin for Chef Inspec that allows testing of all
42
+ Kubernetes API resources
43
+ email:
44
+ - bradgeesaman@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - README.md
50
+ - train-kubernetes-docs.gemspec
51
+ - Gemfile
52
+ - lib/train-kubernetes-docs/connection.rb
53
+ - lib/train-kubernetes-docs/file/linux.rb
54
+ - lib/train-kubernetes-docs/file/linux_immutable_file_check.rb
55
+ - lib/train-kubernetes-docs/file/linux_permissions.rb
56
+ - lib/train-kubernetes-docs/kubectl_client.rb
57
+ - lib/train-kubernetes-docs/platform.rb
58
+ - lib/train-kubernetes-docs/transport.rb
59
+ - lib/train-kubernetes-docs/version.rb
60
+ - lib/train-kubernetes-docs.rb
61
+ homepage: https://github.com/inspec/train-kubernetes-docs
62
+ licenses:
63
+ - Apache-2.0
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '2.4'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.0.14.1
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: DOCS Train Kubernetes
85
+ test_files: []