vanagon 0.15.38 → 0.18.1
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 +4 -4
- data/README.md +48 -23
- data/bin/build +4 -25
- data/bin/build_host_info +4 -17
- data/bin/build_requirements +4 -31
- data/bin/inspect +4 -21
- data/bin/render +4 -22
- data/bin/ship +4 -28
- data/bin/sign +4 -11
- data/bin/vanagon +7 -0
- data/extras/completions/vanagon.bash +38 -0
- data/extras/completions/vanagon.zsh +41 -0
- data/lib/vanagon.rb +1 -1
- data/lib/vanagon/cli.rb +102 -0
- data/lib/vanagon/cli/build.rb +83 -0
- data/lib/vanagon/cli/build_host_info.rb +57 -0
- data/lib/vanagon/cli/build_requirements.rb +68 -0
- data/lib/vanagon/cli/completion.rb +43 -0
- data/lib/vanagon/cli/inspect.rb +73 -0
- data/lib/vanagon/cli/list.rb +75 -0
- data/lib/vanagon/cli/render.rb +59 -0
- data/lib/vanagon/cli/ship.rb +52 -0
- data/lib/vanagon/cli/sign.rb +34 -0
- data/lib/vanagon/driver.rb +35 -27
- data/lib/vanagon/engine/always_be_scheduling.rb +271 -1
- data/lib/vanagon/engine/docker.rb +101 -14
- data/lib/vanagon/engine/pooler.rb +7 -3
- data/lib/vanagon/platform.rb +3 -0
- data/lib/vanagon/platform/deb.rb +2 -0
- data/lib/vanagon/platform/dsl.rb +11 -0
- data/lib/vanagon/utilities.rb +30 -8
- data/resources/osx/postinstall.erb +1 -1
- data/resources/solaris/10/postinstall.erb +1 -1
- data/spec/lib/vanagon/cli_spec.rb +226 -0
- data/spec/lib/vanagon/driver_spec.rb +1 -1
- data/spec/lib/vanagon/engine/always_be_scheduling_spec.rb +113 -1
- data/spec/lib/vanagon/engine/docker_spec.rb +74 -16
- data/spec/lib/vanagon/engine/ec2_spec.rb +2 -0
- data/spec/lib/vanagon/engine/pooler_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- metadata +61 -34
- data/lib/vanagon/optparse.rb +0 -86
- data/spec/lib/vanagon/optparse_spec.rb +0 -64
@@ -10,6 +10,7 @@ class Vanagon
|
|
10
10
|
|
11
11
|
@docker_cmd = Vanagon::Utilities.find_program_on_path('docker')
|
12
12
|
@required_attributes << "docker_image"
|
13
|
+
@required_attributes.delete('ssh_port') if @platform.use_docker_exec
|
13
14
|
end
|
14
15
|
|
15
16
|
# Get the engine name
|
@@ -33,25 +34,16 @@ class Vanagon
|
|
33
34
|
# This method is used to obtain a vm to build upon using
|
34
35
|
# a docker container.
|
35
36
|
# @raise [Vanagon::Error] if a target cannot be obtained
|
36
|
-
def select_target
|
37
|
+
def select_target
|
38
|
+
ssh_args = @platform.use_docker_exec ? '' : "-p #{@platform.ssh_port}:22"
|
37
39
|
extra_args = @platform.docker_run_args.nil? ? [] : @platform.docker_run_args
|
38
40
|
|
39
|
-
Vanagon::Utilities.ex("#{@docker_cmd} run -d --name #{build_host_name}-builder
|
41
|
+
Vanagon::Utilities.ex("#{@docker_cmd} run -d --name #{build_host_name}-builder #{ssh_args} #{extra_args.join(' ')} #{@platform.docker_image}")
|
40
42
|
@target = 'localhost'
|
41
43
|
|
42
|
-
|
43
|
-
# second sleep between errors to account for network resets while SSHD
|
44
|
-
# is starting. Allow a maximum of 5 seconds for SSHD to start.
|
45
|
-
Vanagon::Utilities.retry_with_timeout(5, 5) do
|
46
|
-
begin
|
47
|
-
Vanagon::Utilities.remote_ssh_command("#{@target_user}@#{@target}", 'exit', @platform.ssh_port)
|
48
|
-
rescue StandardError => e
|
49
|
-
sleep(1) # Give SSHD some time to start.
|
50
|
-
raise e
|
51
|
-
end
|
52
|
-
end
|
44
|
+
wait_for_ssh unless @platform.use_docker_exec
|
53
45
|
rescue StandardError => e
|
54
|
-
raise Vanagon::Error.wrap(e, "Something went wrong getting a target vm to build on using
|
46
|
+
raise Vanagon::Error.wrap(e, "Something went wrong getting a target vm to build on using Docker.")
|
55
47
|
end
|
56
48
|
|
57
49
|
# This method is used to tell the vmpooler to delete the instance of the
|
@@ -62,6 +54,101 @@ class Vanagon
|
|
62
54
|
rescue Vanagon::Error => e
|
63
55
|
warn "There was a problem tearing down the docker container #{build_host_name}-builder (#{e.message})."
|
64
56
|
end
|
57
|
+
|
58
|
+
def dispatch(command, return_output = false)
|
59
|
+
if @platform.use_docker_exec
|
60
|
+
docker_exec(command, return_output)
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def ship_workdir(workdir)
|
67
|
+
if @platform.use_docker_exec
|
68
|
+
docker_cp_globs_to("#{workdir}/*", @remote_workdir)
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def retrieve_built_artifact(artifacts_to_fetch, no_packaging)
|
75
|
+
if @platform.use_docker_exec
|
76
|
+
output_path = 'output/'
|
77
|
+
FileUtils.mkdir_p(output_path)
|
78
|
+
unless no_packaging
|
79
|
+
artifacts_to_fetch << "#{@remote_workdir}/output/*"
|
80
|
+
end
|
81
|
+
|
82
|
+
docker_cp_globs_from(artifacts_to_fetch, 'output/')
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Execute a command on a container via docker exec
|
89
|
+
def docker_exec(command, return_output = false)
|
90
|
+
command = command.gsub("'", "'\\\\''")
|
91
|
+
Vanagon::Utilities.local_command("#{@docker_cmd} exec #{build_host_name}-builder /bin/sh -c '#{command}'",
|
92
|
+
return_command_output: return_output)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Copy files between a container and the host
|
96
|
+
def docker_cp(source, target)
|
97
|
+
Vanagon::Utilities.ex("#{@docker_cmd} cp '#{source}' '#{target}'")
|
98
|
+
end
|
99
|
+
|
100
|
+
# Copy files matching a glob pattern from the host to the container
|
101
|
+
def docker_cp_globs_to(globs, container_path)
|
102
|
+
Array(globs).each do |glob|
|
103
|
+
Dir.glob(glob).each do |path|
|
104
|
+
docker_cp(path, "#{build_host_name}-builder:#{container_path}")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Copy files matching a glob pattern from the container to the host
|
110
|
+
#
|
111
|
+
# @note Globs are expanded by running `/bin/sh` in the container, which
|
112
|
+
# may not support the same variety of expressions as Ruby's `Dir.glob`.
|
113
|
+
# For example, `**` may not work.
|
114
|
+
def docker_cp_globs_from(globs, host_path)
|
115
|
+
Array(globs).each do |glob|
|
116
|
+
# Match the behavior of `rsync -r` when both paths are directories
|
117
|
+
# by copying the contents of the directory instead of the directory.
|
118
|
+
glob += '*' if glob.end_with?('/') && host_path.end_with?('/')
|
119
|
+
|
120
|
+
# TODO: This doesn't handle "interesting" paths. E.g. paths with
|
121
|
+
# spaces or other special non-glob characters. This could be
|
122
|
+
# fixed with a variant of `Shellwords.shellescape` that allows
|
123
|
+
# glob characters to pass through.
|
124
|
+
paths = docker_exec(%(for file in #{glob};do [ -e "$file" ] && printf '%s\\0' "${file}";done), true).split("\0")
|
125
|
+
|
126
|
+
paths.each do |path|
|
127
|
+
docker_cp("#{build_host_name}-builder:#{path}", host_path)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Wait for ssh to come up in the container
|
133
|
+
#
|
134
|
+
# Retry 5 times with a 1 second sleep between errors to account for
|
135
|
+
# network resets while SSHD is starting. Allow a maximum of 5 seconds for
|
136
|
+
# SSHD to start.
|
137
|
+
#
|
138
|
+
# @raise [Vanagon::Error] if a SSH connection cannot be established.
|
139
|
+
# @return [void]
|
140
|
+
def wait_for_ssh
|
141
|
+
Vanagon::Utilities.retry_with_timeout(5, 5) do
|
142
|
+
begin
|
143
|
+
Vanagon::Utilities.remote_ssh_command("#{@target_user}@#{@target}", 'exit', @platform.ssh_port)
|
144
|
+
rescue StandardError => e
|
145
|
+
sleep(1) # Give SSHD some time to start.
|
146
|
+
raise e
|
147
|
+
end
|
148
|
+
end
|
149
|
+
rescue StandardError => e
|
150
|
+
raise Vanagon::Error.wrap(e, "SSH was not up in the container after 5 seconds.")
|
151
|
+
end
|
65
152
|
end
|
66
153
|
end
|
67
154
|
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'vanagon/engine/base'
|
2
2
|
require 'yaml'
|
3
3
|
|
4
|
+
### Note this class is deprecated in favor of using the ABS Engine. The pooler has changed it's API with regards to
|
5
|
+
# getting VMs for ondemand provisioning and would need to be updated here.
|
6
|
+
# See DIO-1066
|
7
|
+
|
4
8
|
class Vanagon
|
5
9
|
class Engine
|
6
10
|
class Pooler < Base
|
@@ -10,7 +14,7 @@ class Vanagon
|
|
10
14
|
def initialize(platform, target = nil, **opts)
|
11
15
|
super
|
12
16
|
|
13
|
-
@available_poolers = ["
|
17
|
+
@available_poolers = ["https://vmpooler.delivery.puppetlabs.net", "https://nspooler-service-prod-1.delivery.puppetlabs.net"]
|
14
18
|
@token = load_token
|
15
19
|
@required_attributes << "vmpooler_template"
|
16
20
|
end
|
@@ -63,7 +67,7 @@ class Vanagon
|
|
63
67
|
|
64
68
|
# Read a vmpooler token from the yaml formatted vmfloaty config,
|
65
69
|
# as outlined by the vmfloaty project:
|
66
|
-
# https://github.com/
|
70
|
+
# https://github.com/puppetlabs/vmfloaty
|
67
71
|
#
|
68
72
|
# @return [String, nil] the vmfloaty vmpooler token value
|
69
73
|
def read_vmfloaty_token(path = "~/.vmfloaty.yml")
|
@@ -75,7 +79,7 @@ class Vanagon
|
|
75
79
|
end
|
76
80
|
private :read_vmfloaty_token
|
77
81
|
|
78
|
-
# This method is used to obtain a vm to build upon using
|
82
|
+
# This method is used to obtain a vm to build upon using Puppet's internal
|
79
83
|
# vmpooler (https://github.com/puppetlabs/vmpooler) or other pooler technologies
|
80
84
|
# leveraging the same API
|
81
85
|
# @raise [Vanagon::Error] if a target cannot be obtained
|
data/lib/vanagon/platform.rb
CHANGED
@@ -115,6 +115,7 @@ class Vanagon
|
|
115
115
|
# Docker engine specific
|
116
116
|
attr_accessor :docker_image
|
117
117
|
attr_accessor :docker_run_args
|
118
|
+
attr_accessor :use_docker_exec
|
118
119
|
|
119
120
|
# AWS engine specific
|
120
121
|
attr_accessor :aws_ami
|
@@ -238,6 +239,8 @@ class Vanagon
|
|
238
239
|
@copy ||= "cp"
|
239
240
|
@shasum ||= "sha1sum"
|
240
241
|
|
242
|
+
@use_docker_exec = false
|
243
|
+
|
241
244
|
# Our first attempt at defining metadata about a platform
|
242
245
|
@cross_compiled ||= false
|
243
246
|
@valid_operators ||= ['<', '>', '<=', '>=', '=']
|
data/lib/vanagon/platform/deb.rb
CHANGED
@@ -13,6 +13,8 @@ class Vanagon
|
|
13
13
|
copy_extensions = '*.deb'
|
14
14
|
end
|
15
15
|
pkg_arch_opt = project.noarch ? "" : "-a#{@architecture}"
|
16
|
+
pkg_arch_opt = '-aarm64' if pkg_arch_opt == '-aaarch64'
|
17
|
+
|
16
18
|
["mkdir -p output/#{target_dir}",
|
17
19
|
"mkdir -p $(tempdir)/#{project.name}-#{project.version}",
|
18
20
|
"cp #{project.name}-#{project.version}.tar.gz $(tempdir)/#{project.name}_#{project.version}.orig.tar.gz",
|
data/lib/vanagon/platform/dsl.rb
CHANGED
@@ -284,6 +284,17 @@ class Vanagon
|
|
284
284
|
@platform.docker_run_args = Array(args)
|
285
285
|
end
|
286
286
|
|
287
|
+
# Specify whether to use Docker exec instead of SSH to run commands
|
288
|
+
#
|
289
|
+
# This also causes Vanagon to use `docker cp` instead of `rsync` when
|
290
|
+
# copying files.
|
291
|
+
#
|
292
|
+
# @param bool [Boolean] a boolean value indicating whether to use
|
293
|
+
# `docker exec` and `docker cp` over `ssh` and `rsync`.
|
294
|
+
def use_docker_exec(bool)
|
295
|
+
@platform.use_docker_exec = bool
|
296
|
+
end
|
297
|
+
|
287
298
|
# Set the ami for the platform to use
|
288
299
|
#
|
289
300
|
# @param ami [String] the ami id used.
|
data/lib/vanagon/utilities.rb
CHANGED
@@ -39,17 +39,16 @@ class Vanagon
|
|
39
39
|
end
|
40
40
|
|
41
41
|
# Simple wrapper around Net::HTTP. Will make a request of the given type to
|
42
|
-
# the given url and return the
|
42
|
+
# the given url and return the response object
|
43
43
|
#
|
44
44
|
# @param url [String] The url to make the request against (needs to be parsable by URI
|
45
45
|
# @param type [String] One of the supported request types (currently 'get', 'post', 'delete')
|
46
46
|
# @param payload [String] The request body data payload used for POST and PUT
|
47
47
|
# @param header [Hash] Send additional information in the HTTP request header
|
48
|
-
# @return [
|
48
|
+
# @return [Net::HTTPAccepted] The response object
|
49
49
|
# @raise [RuntimeError, Vanagon::Error] an exception is raised if the
|
50
|
-
# action is not supported, or if there is a problem with the http request
|
51
|
-
|
52
|
-
def http_request(url, type, payload = {}.to_json, header = nil) # rubocop:disable Metrics/AbcSize
|
50
|
+
# action is not supported, or if there is a problem with the http request
|
51
|
+
def http_request_generic(url, type, payload = {}.to_json, header = nil) # rubocop:disable Metrics/AbcSize
|
53
52
|
uri = URI.parse(url)
|
54
53
|
http = Net::HTTP.new(uri.host, uri.port)
|
55
54
|
http.use_ssl = true if uri.scheme == 'https'
|
@@ -77,14 +76,37 @@ class Vanagon
|
|
77
76
|
end
|
78
77
|
|
79
78
|
response = http.request(request)
|
80
|
-
|
81
|
-
JSON.parse(response.body)
|
79
|
+
response
|
82
80
|
rescue Errno::ETIMEDOUT, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
|
83
81
|
EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
|
84
82
|
Net::ProtocolError => e
|
85
83
|
raise Vanagon::Error.wrap(e, "Problem reaching #{url}. Is #{uri.host} down?")
|
84
|
+
end
|
85
|
+
|
86
|
+
# uses http_request_generic and returns the body as parsed by JSON.
|
87
|
+
# @param url [String] The url to make the request against (needs to be parsable by URI
|
88
|
+
# @param type [String] One of the supported request types (currently 'get', 'post', 'delete')
|
89
|
+
# @param payload [String] The request body data payload used for POST and PUT
|
90
|
+
# @param header [Hash] Send additional information in the HTTP request header
|
91
|
+
# @return [Hash] The response in JSON format
|
92
|
+
# @raise [RuntimeError, Vanagon::Error] an exception is raised if the response
|
93
|
+
# body cannot be parsed as JSON
|
94
|
+
def http_request(url, type, payload = {}.to_json, header = nil)
|
95
|
+
response = http_request_generic(url, type, payload, header)
|
96
|
+
JSON.parse(response.body)
|
86
97
|
rescue JSON::ParserError => e
|
87
|
-
raise Vanagon::Error.wrap(e, "#{
|
98
|
+
raise Vanagon::Error.wrap(e, "#{url} handed us a response that doesn't look like JSON.")
|
99
|
+
end
|
100
|
+
|
101
|
+
# uses http_request_generic and returns the response code.
|
102
|
+
# @param url [String] The url to make the request against (needs to be parsable by URI
|
103
|
+
# @param type [String] One of the supported request types (currently 'get', 'post', 'delete')
|
104
|
+
# @param payload [String] The request body data payload used for POST and PUT
|
105
|
+
# @param header [Hash] Send additional information in the HTTP request header
|
106
|
+
# @return [String] The response code eg 202, 200 etc
|
107
|
+
def http_request_code(url, type, payload = {}.to_json, header = nil)
|
108
|
+
response = http_request_generic(url, type, payload, header)
|
109
|
+
response.code
|
88
110
|
end
|
89
111
|
|
90
112
|
# Similar to rake's sh, the passed command will be executed and an
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<%- get_configfiles.each do |config|
|
6
6
|
dest_file = config.path.gsub(/\.pristine$/, '') -%>
|
7
7
|
|
8
|
-
if [ -f "<%= dest_file %>" ]; then
|
8
|
+
if [ -f "<%= dest_file %>" ] && ! diff "<%= config.path %>" "<%= dest_file %>" > /dev/null; then
|
9
9
|
echo "Detected file at '<%= dest_file %>'; updated file at '<%= config.path %>'."
|
10
10
|
else
|
11
11
|
mv '<%= config.path %>' '<%= dest_file %>'
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<%- get_configfiles.each do |config|
|
10
10
|
dest_file = config.path.gsub(/\.pristine$/, '') -%>
|
11
11
|
|
12
|
-
if [ -f "<%= dest_file %>" ]; then
|
12
|
+
if [ -f "<%= dest_file %>" ] && ! diff "<%= config.path %>" "<%= dest_file %>" > /dev/null; then
|
13
13
|
echo "Detected file at '<%= dest_file %>'; updated file at '<%= config.path %>'."
|
14
14
|
else
|
15
15
|
cp -pr '<%= config.path %>' '<%= dest_file %>'
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'vanagon/cli'
|
2
|
+
|
3
|
+
##
|
4
|
+
## Ignore the CLI calling 'exit'
|
5
|
+
##
|
6
|
+
RSpec.configure do |rspec|
|
7
|
+
rspec.around(:example) do |ex|
|
8
|
+
begin
|
9
|
+
ex.run
|
10
|
+
rescue SystemExit => e
|
11
|
+
puts "Got SystemExit: #{e.inspect}. Ignoring"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Vanagon::CLI do
|
17
|
+
describe "options that don't take a value" do
|
18
|
+
[:skipcheck, :verbose].each do |flag|
|
19
|
+
it "can create an option parser that accepts the #{flag} flag" do
|
20
|
+
subject = described_class.new
|
21
|
+
expect(subject.parse(%W[build --#{flag} project platform])).to have_key(flag)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "short options" do
|
26
|
+
[["v", :verbose]].each do |short, long|
|
27
|
+
it "maps the short option #{short} to #{long}" do
|
28
|
+
subject = described_class.new
|
29
|
+
expect(subject.parse(%W[build -#{short} project platform])).to include(long => true)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "options that only allow limited values" do
|
36
|
+
[[:preserve, ["always", "never", "on-failure"]]].each do |option, values|
|
37
|
+
values.each do |value|
|
38
|
+
it "can create a parser that accepts \"--#{option} #{value}\"" do
|
39
|
+
subject = described_class.new
|
40
|
+
expect(subject.parse(%W[build --#{option} #{value} project platform]))
|
41
|
+
.to include(option => value.to_sym)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
[[:preserve, ["bad-argument"]]].each do |option, values|
|
46
|
+
values.each do |value|
|
47
|
+
it "rejects the bad argument \"--#{option} #{value}\"" do
|
48
|
+
subject = described_class.new
|
49
|
+
expect{subject.parse(%W[build --#{option} #{value} project platform])}
|
50
|
+
.to raise_error(Vanagon::InvalidArgument)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
it "preserve defaults to :on-failure" do
|
55
|
+
subject = described_class.new
|
56
|
+
expect(subject.parse([])).to include(:preserve => :'on-failure')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
describe "options that take a value" do
|
62
|
+
[:workdir, :configdir, :engine].each do |option|
|
63
|
+
it "can create an option parser that accepts the #{option} option" do
|
64
|
+
subject = described_class.new
|
65
|
+
expect(subject.parse(%W[build --#{option} hello project platform]))
|
66
|
+
.to include(option => "hello")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "short options" do
|
71
|
+
[["w", :workdir], ["c", :configdir], ["e", :engine]].each do |short, long|
|
72
|
+
it "maps the short option #{short} to #{long}" do
|
73
|
+
subject = described_class.new
|
74
|
+
expect(subject.parse(%W[build -#{short} hello project platform]))
|
75
|
+
.to include(long => "hello")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe Vanagon::CLI::List do
|
83
|
+
let(:cli) { Vanagon::CLI::List.new }
|
84
|
+
|
85
|
+
describe "#output" do
|
86
|
+
let(:list) { ['a', 'b', 'c']}
|
87
|
+
it "returns an array if space is false" do
|
88
|
+
expect(cli.output(list, false)).to eq(list)
|
89
|
+
end
|
90
|
+
it "returns space separated if space is true" do
|
91
|
+
expect(cli.output(list, true)).to eq('a b c')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#run" do
|
96
|
+
let(:projects){ ['foo', 'bar', 'baz'] }
|
97
|
+
let(:platforms){ ['1', '2', '3'] }
|
98
|
+
let(:output_both){
|
99
|
+
"- Projects
|
100
|
+
foo
|
101
|
+
bar
|
102
|
+
baz
|
103
|
+
|
104
|
+
- Platforms
|
105
|
+
1
|
106
|
+
2
|
107
|
+
3
|
108
|
+
"
|
109
|
+
}
|
110
|
+
context "specs with standard config path" do
|
111
|
+
before(:each) do
|
112
|
+
expect(Dir).to receive(:exist?)
|
113
|
+
.with("#{File.join(Dir.pwd, 'configs', 'platforms')}")
|
114
|
+
.and_return(true)
|
115
|
+
expect(Dir).to receive(:exist?)
|
116
|
+
.with("#{File.join(Dir.pwd, 'configs', 'projects')}")
|
117
|
+
.and_return(true)
|
118
|
+
expect(Dir).to receive(:children)
|
119
|
+
.with("#{File.join(Dir.pwd, 'configs', 'projects')}")
|
120
|
+
.and_return(projects)
|
121
|
+
expect(Dir).to receive(:children)
|
122
|
+
.with("#{File.join(Dir.pwd, 'configs', 'platforms')}")
|
123
|
+
.and_return(platforms)
|
124
|
+
end
|
125
|
+
let(:options_empty) { {
|
126
|
+
nil=>false,
|
127
|
+
:configdir=>"#{Dir.pwd}/configs",
|
128
|
+
:platforms=>false,
|
129
|
+
:projects=>false,
|
130
|
+
:use_spaces=>false
|
131
|
+
} }
|
132
|
+
let(:options_platforms_only) { {
|
133
|
+
nil=>false,
|
134
|
+
:configdir=>"#{Dir.pwd}/configs",
|
135
|
+
:platforms=>true,
|
136
|
+
:projects=>false,
|
137
|
+
:use_spaces=>false
|
138
|
+
} }
|
139
|
+
let(:options_projects_only) { {
|
140
|
+
nil=>false,
|
141
|
+
:configdir=>"#{Dir.pwd}/configs",
|
142
|
+
:platforms=>false,
|
143
|
+
:projects=>true,
|
144
|
+
:use_spaces=>false
|
145
|
+
} }
|
146
|
+
let(:options_space_only) { {
|
147
|
+
nil=>false,
|
148
|
+
:configdir=>"#{Dir.pwd}/configs",
|
149
|
+
:platforms=>false,
|
150
|
+
:projects=>false,
|
151
|
+
:use_spaces=>true
|
152
|
+
} }
|
153
|
+
|
154
|
+
it "outputs projects and platforms with no options passed" do
|
155
|
+
expect do
|
156
|
+
cli.run(options_empty)
|
157
|
+
end.to output(output_both).to_stdout
|
158
|
+
end
|
159
|
+
|
160
|
+
let(:output_both_space){
|
161
|
+
"- Projects
|
162
|
+
foo bar baz
|
163
|
+
|
164
|
+
- Platforms
|
165
|
+
1 2 3
|
166
|
+
"
|
167
|
+
}
|
168
|
+
it "outputs projects and platforms space separated" do
|
169
|
+
expect do
|
170
|
+
cli.run(options_space_only)
|
171
|
+
end.to output(output_both_space).to_stdout
|
172
|
+
end
|
173
|
+
|
174
|
+
let(:output_platforms){
|
175
|
+
"- Platforms
|
176
|
+
1
|
177
|
+
2
|
178
|
+
3
|
179
|
+
"
|
180
|
+
}
|
181
|
+
it "outputs only platforms when platforms is passed" do
|
182
|
+
expect do
|
183
|
+
cli.run(options_platforms_only)
|
184
|
+
end.to output(output_platforms).to_stdout
|
185
|
+
end
|
186
|
+
|
187
|
+
let(:output_projects){
|
188
|
+
"- Projects
|
189
|
+
foo
|
190
|
+
bar
|
191
|
+
baz
|
192
|
+
"
|
193
|
+
}
|
194
|
+
it "outputs only projects when projects is passed" do
|
195
|
+
expect do
|
196
|
+
cli.run(options_projects_only)
|
197
|
+
end.to output(output_projects).to_stdout
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "spec with a configdir specified" do
|
202
|
+
let(:options_configdir) { {
|
203
|
+
nil=>false,
|
204
|
+
:configdir=> '/configs',
|
205
|
+
:platforms=>false,
|
206
|
+
:projects=>false,
|
207
|
+
:use_spaces=>false} }
|
208
|
+
it "it successfully takes the configs directory" do
|
209
|
+
expect(Dir).to receive(:exist?).with('/configs' + '/platforms')
|
210
|
+
.and_return(true)
|
211
|
+
expect(Dir).to receive(:exist?).with('/configs' + '/projects')
|
212
|
+
.and_return(true)
|
213
|
+
expect(Dir).to receive(:children).with('/configs' + '/projects')
|
214
|
+
.and_return(projects)
|
215
|
+
expect(Dir).to receive(:children).with('/configs' + '/platforms')
|
216
|
+
.and_return(platforms)
|
217
|
+
expect do
|
218
|
+
cli.run(options_configdir)
|
219
|
+
end.to output(output_both).to_stdout
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
|