train-vmware 0.1.4

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 238630d02bbca3ff3ba2bf7c27577bcb291222c3dbed50918601ac2100293989
4
+ data.tar.gz: 900a9ccf490ad20969bd1bda9e8cab0e309d44535df143cd665b8061bbd30448
5
+ SHA512:
6
+ metadata.gz: '09d46aaad75d424f1f3042627dc08cf4ba3b9205a97d72d2fedbe68663cccc7f7e75fa37836828059e6b6d5c49127c9944d235e56b7be4783f66d18198feb354'
7
+ data.tar.gz: 31f060e25871a77e487731566e490a2899f4c7f892395adbd7b582ae7d96ab3eb171e2a085c7da4ec9e1c4633573ca26eaf8edc4f9e2b7fba8d69a84d5856a18
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "https://rubygems.org"
2
+
3
+ # This is Gemfile, which is used by bundler
4
+ # to ensure a coherent set of gems is installed.
5
+ # This file lists dependencies needed when outside
6
+ # of a gem (the gemspec lists deps for gem deployment)
7
+
8
+ # Bundler should refer to the gemspec for any dependencies.
9
+ gemspec
10
+
11
+ # Remaining group is only used for development.
12
+ group :development do
13
+ gem "bundler"
14
+ gem "byebug"
15
+ gem "inspec", ">= 2.2.112" # We need InSpec for the test harness while developing.
16
+ gem "minitest"
17
+ gem "rake"
18
+ gem "rubocop", "= 0.49.1" # Need to keep in sync with main InSpec project, so config files will work
19
+ end
data/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # train-vmware
2
+
3
+ InSpec Train Plugin for vSphere
4
+
5
+
6
+ gem build .\train-vmware.gemspec
7
+
8
+ gem push .\train-vmware-0.1.1.gem
@@ -0,0 +1,175 @@
1
+ # This allow us to inherit from Train::Plugins::Transport::BaseConnection
2
+ require "train"
3
+ require "open3"
4
+ require "ostruct" unless defined?(OpenStruct)
5
+ require "json" unless defined?(JSON)
6
+ require "mkmf"
7
+
8
+ module TrainPlugins
9
+ module VMware
10
+ # You must inherit from BaseConnection.
11
+ class Connection < Train::Plugins::Transport::BaseConnection
12
+ POWERSHELL_PROMPT_REGEX = /PS\s.*> $/.freeze
13
+
14
+ def initialize(options)
15
+ super(options)
16
+
17
+ options[:viserver] = options[:viserver] || options[:host]
18
+ options[:username] = options[:username] || options[:user]
19
+
20
+ @username = options[:username]
21
+ @viserver = options[:viserver]
22
+ @session = nil
23
+ @stdout_buffer = ""
24
+ @stderr_buffer = ""
25
+
26
+ @powershell_binary = detect_powershell_binary
27
+
28
+ if @powershell_binary == :powershell
29
+ require "train/transports/local"
30
+ @powershell = Train::Transports::Local::Connection.new(options)
31
+ end
32
+
33
+ if options[:insecure] == true
34
+ run_command_via_connection("Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False")
35
+ end
36
+
37
+ @platform_details = {
38
+ release: "vmware-powercli-#{powercli_version}",
39
+ }
40
+
41
+ connect
42
+ end
43
+
44
+ def connect
45
+ login_command = "Connect-VIServer #{options[:viserver]} -User #{options[:username]} -Password #{options[:password]} | Out-Null"
46
+ result = run_command_via_connection(login_command)
47
+
48
+ if result.exit_status != 0
49
+ message = "Unable to connect to VIServer at #{options[:viserver]}. "
50
+ case result.stderr
51
+ when /Invalid server certificate/
52
+ message += "Certification verification failed. Please use `--insecure` or set `Set-PowerCLIConfiguration -InvalidCertificateAction Ignore` in PowerShell"
53
+ when /incorrect user name or password/
54
+ message += "Incorrect username or password"
55
+ else
56
+ message += result.stderr.gsub(/-Password .*\s/, "-Password REDACTED")
57
+ end
58
+
59
+ raise message
60
+ end
61
+ end
62
+
63
+ def platform
64
+ force_platform!("vmware", @platform_details)
65
+ end
66
+
67
+ def run_command_via_connection(cmd, &_data_handler)
68
+ if @powershell_binary == :pwsh
69
+ result = parse_pwsh_output(cmd)
70
+
71
+ # Attach exit status to result
72
+ exit_status = parse_pwsh_output("echo $?").stdout.chomp
73
+ result.exit_status = exit_status == "True" ? 0 : 1
74
+
75
+ result
76
+ else
77
+ @powershell.run_command(cmd)
78
+ end
79
+ end
80
+
81
+ def unique_identifier
82
+ uuid_command = "(Get-VMHost | Get-View).hardware.systeminfo.uuid"
83
+ run_command_via_connection(uuid_command).stdout.chomp
84
+ end
85
+
86
+ def uri
87
+ "vmware://#{@username}@#{@viserver}"
88
+ end
89
+
90
+ private
91
+
92
+ def detect_powershell_binary
93
+ if find_executable0("powershell")
94
+ :powershell
95
+ elsif find_executable0("pwsh")
96
+ :pwsh
97
+ else
98
+ raise "Cannot find PowerShell binary, is `pwsh` installed?"
99
+ end
100
+ end
101
+
102
+ # Read from stdout pipe until prompt is received
103
+ def flush_stdout(pipe)
104
+ @stdout_buffer += pipe.read_nonblock(1) while @stdout_buffer !~ POWERSHELL_PROMPT_REGEX
105
+ @stdout_buffer
106
+ rescue IO::EAGAINWaitReadable
107
+ # We cannot know when the stdout pipe is finished so we keep reading
108
+ retry
109
+ ensure
110
+ @stdout_buffer = ""
111
+ end
112
+
113
+ # This must be called after `flush_stdout` to ensure buffer is full
114
+ def flush_stderr(pipe)
115
+ loop do
116
+ @stderr_buffer += pipe.read_nonblock(1)
117
+ end
118
+ rescue IO::EAGAINWaitReadable
119
+ # If `flush_stderr` is ran after reading stdout we know that all of
120
+ # stderr is in the pipe. Thus, we can return the buffer once the pipe
121
+ # is unreadable.
122
+ @stderr_buffer
123
+ ensure
124
+ @stderr_buffer = ""
125
+ end
126
+
127
+ def parse_pwsh_output(cmd)
128
+ session.stdin.puts(cmd)
129
+
130
+ stdout = flush_stdout(session.stdout)
131
+
132
+ # Remove stdin from stdout (including trailing newline)
133
+ stdout.slice!(0, cmd.length + 1)
134
+
135
+ # Remove prompt from stdout
136
+ stdout.gsub!(POWERSHELL_PROMPT_REGEX, "")
137
+
138
+ # Grab stderr
139
+ stderr = flush_stderr(session.stderr)
140
+
141
+ CommandResult.new(
142
+ stdout,
143
+ stderr,
144
+ nil # exit_status is attached in `run_command_via_connection`
145
+ )
146
+ end
147
+
148
+ def powercli_version
149
+ version_command = "[string](Get-Module -Name VMware.PowerCLI -ListAvailable | Select -ExpandProperty Version)"
150
+ result = run_command_via_connection(version_command)
151
+ if result.stdout.empty? || result.exit_status != 0
152
+ raise "Unable to determine PowerCLI Module version, is it installed?"
153
+ end
154
+
155
+ result.stdout.chomp
156
+ end
157
+
158
+ def session
159
+ return @session unless @session.nil?
160
+
161
+ stdin, stdout, stderr = Open3.popen3("pwsh")
162
+
163
+ # Remove leading prompt and intro text
164
+ flush_stdout(stdout)
165
+
166
+ @session = OpenStruct.new
167
+ @session.stdin = stdin
168
+ @session.stdout = stdout
169
+ @session.stderr = stderr
170
+
171
+ @session
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,21 @@
1
+ require "train-vmware/connection"
2
+
3
+ # Train Plugins v1 are usually declared under the TrainPlugins namespace.
4
+ # Each plugin has three components: Transport, Connection, and (optionally) Platform.
5
+ # We'll only define the Transport here, but we'll refer to the others.
6
+
7
+ module TrainPlugins
8
+ module VMware
9
+ class VMware < Train.plugin(1)
10
+ name "vmware"
11
+ option :viserver, default: proc { ENV["VISERVER"] }
12
+ option :username, default: proc { ENV["VISERVER_USERNAME"] }
13
+ option :password, default: proc { ENV["VISERVER_PASSWORD"] }
14
+ option :insecure, default: false
15
+
16
+ def connection(_ = nil)
17
+ @connection ||= Connection.new(@options)
18
+ end
19
+ end
20
+ end
21
+ 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 VMware
8
+ VERSION = "0.1.4".freeze
9
+ end
10
+ end
@@ -0,0 +1,20 @@
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_relative "train-vmware/version"
16
+
17
+ # A train plugin has three components: Transport, Connection, and (optionally) Platform.
18
+ # Transport acts as the glue.
19
+ require_relative "train-vmware/transport"
20
+ require_relative "train-vmware/connection"
@@ -0,0 +1,45 @@
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", __dir__)
8
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
9
+ require "train-vmware/version"
10
+
11
+ Gem::Specification.new do |spec|
12
+ # Importantly, all Train plugins must be prefixed with `train-`
13
+ spec.name = "train-vmware"
14
+
15
+ # It is polite to namespace your plugin under TrainPlugins::YourPluginInCamelCase
16
+ spec.version = TrainPlugins::VMware::VERSION
17
+ spec.authors = ["VMware"]
18
+ spec.email = ["none@none.com"]
19
+ spec.summary = "Train Plugin for VMware PowerCLI"
20
+ spec.description = "Plugin version of the native VMware Transport in InSpec Train"
21
+ spec.homepage = ""
22
+ spec.license = "Apache-2.0"
23
+
24
+ # Though complicated-looking, this is pretty standard for a gemspec.
25
+ # It just filters what will actually be packaged in the gem (leaving
26
+ # out tests, etc)
27
+ spec.files = %w{
28
+ README.md train-vmware.gemspec Gemfile
29
+ } + Dir.glob(
30
+ "lib/**/*", File::FNM_DOTMATCH
31
+ ).reject { |f| File.directory?(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ # If you rely on any other gems, list them here with any constraints.
35
+ # This is how `inspec plugin install` is able to manage your dependencies.
36
+ # For example, perhaps you are writing a thing that talks to AWS, and you
37
+ # want to ensure you have `aws-sdk` in a certain version.
38
+
39
+ # If you only need certain gems during development or testing, list
40
+ # them in Gemfile, not here.
41
+ # Do not list inspec as a dependency of the train plugin.
42
+
43
+ # All plugins should mention train, > 1.4
44
+ spec.add_dependency "train", "~> 3.8"
45
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: train-vmware
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - VMware
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-02-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: train
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.8'
27
+ description: Plugin version of the native VMware Transport in InSpec Train
28
+ email:
29
+ - none@none.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - README.md
36
+ - lib/train-vmware.rb
37
+ - lib/train-vmware/connection.rb
38
+ - lib/train-vmware/transport.rb
39
+ - lib/train-vmware/version.rb
40
+ - train-vmware.gemspec
41
+ homepage: ''
42
+ licenses:
43
+ - Apache-2.0
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubygems_version: 3.2.32
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Train Plugin for VMware PowerCLI
64
+ test_files: []