covalence 0.0.1 → 0.7.9.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +164 -0
- data/README.md +489 -19
- data/TODO.md +14 -0
- data/lib/covalence.rb +41 -0
- data/lib/covalence/core/bootstrap.rb +8 -0
- data/lib/covalence/core/cli_wrappers/packer.yml +9 -0
- data/lib/covalence/core/cli_wrappers/packer_cli.rb +27 -0
- data/lib/covalence/core/cli_wrappers/popen_wrapper.rb +123 -0
- data/lib/covalence/core/cli_wrappers/terraform.yml +39 -0
- data/lib/covalence/core/cli_wrappers/terraform_cli.rb +119 -0
- data/lib/covalence/core/data_stores/hiera.rb +50 -0
- data/lib/covalence/core/entities/context.rb +38 -0
- data/lib/covalence/core/entities/environment.rb +24 -0
- data/lib/covalence/core/entities/input.rb +112 -0
- data/lib/covalence/core/entities/stack.rb +74 -0
- data/lib/covalence/core/entities/state_store.rb +65 -0
- data/lib/covalence/core/repositories/context_repository.rb +30 -0
- data/lib/covalence/core/repositories/environment_repository.rb +92 -0
- data/lib/covalence/core/repositories/input_repository.rb +56 -0
- data/lib/covalence/core/repositories/stack_repository.rb +89 -0
- data/lib/covalence/core/repositories/state_store_repository.rb +31 -0
- data/lib/covalence/core/services/hiera_syntax_service.rb +19 -0
- data/lib/covalence/core/services/packer_stack_tasks.rb +104 -0
- data/lib/covalence/core/services/terraform_stack_tasks.rb +212 -0
- data/lib/covalence/core/state_stores/atlas.rb +157 -0
- data/lib/covalence/core/state_stores/consul.rb +153 -0
- data/lib/covalence/core/state_stores/s3.rb +147 -0
- data/lib/covalence/environment_tasks.rb +328 -0
- data/lib/covalence/helpers/shell_interpolation.rb +28 -0
- data/lib/covalence/helpers/spec_dependencies.rb +21 -0
- data/lib/covalence/rake/rspec/envs_spec.rb +75 -0
- data/lib/covalence/rake/rspec/yaml_spec.rb +14 -0
- data/lib/covalence/spec_tasks.rb +59 -0
- data/lib/covalence/version.rb +3 -0
- metadata +344 -26
- data/.gitignore +0 -9
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/lib/prometheus_unifio.rb +0 -5
- data/lib/prometheus_unifio/version.rb +0 -3
- data/prometheus_unifio.gemspec +0 -32
data/TODO.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
- individual state-stores: would be nice if it had something to describe the backing input types
|
2
|
+
- consider httparty vs rest-client
|
3
|
+
- Use the invalid yaml syntax to figure out more things in rake tasks that needs to be lazy loaded
|
4
|
+
- Like the idea of maybe splitting out the syntax elements to a different gem. Gem installation & management would become easier
|
5
|
+
- missing spec around reports?
|
6
|
+
- Look into muting the reporter as dev default: https://github.com/ci-reporter/ci_reporter
|
7
|
+
- Look into getting HieraDB wrapper to read .yml files
|
8
|
+
- Terraform CLI in docker probably isn't going anywhere, might need to figure out a nicer way of handling this
|
9
|
+
- Use covalence to do verification on the open source stacks:
|
10
|
+
- terraform-aws-openvpn
|
11
|
+
- Plugin architecture
|
12
|
+
- rollup tasks in terraform need to check terraform only tasks
|
13
|
+
- stack_repository: backfill packer related tests
|
14
|
+
- packer: utils to gen uuids as inputs?
|
data/lib/covalence.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "covalence/version"
|
2
|
+
require "logger"
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
|
5
|
+
if %w(development test).include?(ENV['RAKE_ENV'])
|
6
|
+
require 'byebug'
|
7
|
+
require 'awesome_print'
|
8
|
+
end
|
9
|
+
|
10
|
+
# :reek:TooManyConstants
|
11
|
+
module Covalence
|
12
|
+
# Configurable constants
|
13
|
+
#TODO: look into how WORKSPACE is being used, maybe this can just be an internal ROOT and make CONFIG not depend on WORKSPACE
|
14
|
+
WORKSPACE = File.absolute_path(ENV['COVALENCE_WORKSPACE'] || '.')
|
15
|
+
CONFIG = File.join(WORKSPACE, (ENV['COVALENCE_CONFIG'] || 'covalence.yaml'))
|
16
|
+
# TODO: could use better naming
|
17
|
+
PACKER = File.absolute_path(File.join(WORKSPACE, (ENV['COVALENCE_PACKER_DIR'] || '.')))
|
18
|
+
TERRAFORM = File.absolute_path(File.join(WORKSPACE, (ENV['COVALENCE_TERRAFORM_DIR'] || '.')))
|
19
|
+
TEST_ENVS = (ENV['COVALENCE_TEST_ENVS'] || "").split(',')
|
20
|
+
# Reserved namespace including default ci and spec
|
21
|
+
RESERVED_NS = [(ENV['COVALENCE_RESERVED_NAMESPACE'] || "").split(','), 'ci', 'spec']
|
22
|
+
|
23
|
+
TERRAFORM_CMD = ENV['TERRAFORM_CMD'] || "terraform"
|
24
|
+
TERRAFORM_VERSION = ENV['TERRAFORM_VERSION'] || `#{TERRAFORM_CMD} version`.split("\n", 2)[0].gsub('Terraform v','')
|
25
|
+
TERRAFORM_PLUGIN_CACHE = File.absolute_path("#{ENV['TF_PLUGIN_CACHE_DIR']}/linux_amd64" || "#{ENV['HOME']}/.terraform.d/plugin-cache/linus_amd64")
|
26
|
+
|
27
|
+
PACKER_CMD = ENV['PACKER_CMD'] || "packer"
|
28
|
+
|
29
|
+
# No-op shell command. Should not need to modify for most unix shells.
|
30
|
+
DRY_RUN_CMD = (ENV['COVALENCE_DRY_RUN_CMD'] || ":")
|
31
|
+
DEBUG_CLI = (ENV['COVALENCE_DEBUG'] || 'false') =~ (/(true|t|yes|y|1)$/i)
|
32
|
+
|
33
|
+
#DOCKER_ENV_FILE
|
34
|
+
|
35
|
+
# Internal constants
|
36
|
+
GEM_ROOT = File.expand_path('covalence', File.dirname(__FILE__)).freeze
|
37
|
+
# look into logger-colors
|
38
|
+
LOGGER = Logger.new(STDOUT)
|
39
|
+
LOG_LEVEL = String(ENV['COVALENCE_LOG'] || "warn").upcase
|
40
|
+
LOGGER.level = Logger.const_get(LOG_LEVEL)
|
41
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require_relative '../../covalence'
|
4
|
+
|
5
|
+
Dir[File.expand_path("cli_wrappers/**/*.rb", File.dirname(__FILE__))].sort.each { |file| require file }
|
6
|
+
Dir[File.expand_path("entities/**/*.rb", File.dirname(__FILE__))].sort.each { |file| require file }
|
7
|
+
Dir[File.expand_path("repositories/**/*.rb", File.dirname(__FILE__))].sort.each { |file| require file }
|
8
|
+
Dir[File.expand_path("services/**/*.rb", File.dirname(__FILE__))].sort.each { |file| require file }
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Covalence
|
2
|
+
class PackerCli
|
3
|
+
class << self
|
4
|
+
def require_init()
|
5
|
+
cmds_yml = File.expand_path("packer.yml", __dir__)
|
6
|
+
init_packer_cmds(cmds_yml)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def init_packer_cmds(file)
|
11
|
+
definition = YAML.load_file(file)
|
12
|
+
|
13
|
+
definition['commands'].each do |cmd, _|
|
14
|
+
packer_cmd = "packer_#{cmd}"
|
15
|
+
|
16
|
+
next if respond_to?(packer_cmd.to_sym)
|
17
|
+
define_singleton_method(packer_cmd) do |template, args: ''|
|
18
|
+
output = PopenWrapper.run([Covalence::PACKER_CMD, cmd], template, args)
|
19
|
+
(output == 0)
|
20
|
+
end #define_singleton_method
|
21
|
+
end # definition
|
22
|
+
end
|
23
|
+
end # class << self
|
24
|
+
end #PackerCli
|
25
|
+
end
|
26
|
+
|
27
|
+
Covalence::PackerCli.require_init
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'highline'
|
3
|
+
|
4
|
+
require_relative '../../../covalence'
|
5
|
+
|
6
|
+
module Covalence
|
7
|
+
class PopenWrapper
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def run(cmds, path, args,
|
11
|
+
stdin_io: STDIN,
|
12
|
+
stdout_io: STDOUT,
|
13
|
+
stderr_io: STDERR,
|
14
|
+
debug: Covalence::DEBUG_CLI,
|
15
|
+
dry_run: false,
|
16
|
+
ignore_exitcode: false)
|
17
|
+
|
18
|
+
# TODO: implement path prefix for the docker runs, see @tf_cmd
|
19
|
+
cmd_string = [*cmds]
|
20
|
+
# TODO: cmd escape issues with -var.
|
21
|
+
cmd_string += [*args] unless args.blank?
|
22
|
+
cmd_string << path unless path.blank?
|
23
|
+
|
24
|
+
#TODO debug command string maybe
|
25
|
+
#TODO debug command args maybe
|
26
|
+
|
27
|
+
run_cmd = cmd_string.join(' ')
|
28
|
+
print_cmd_string(run_cmd)
|
29
|
+
if dry_run
|
30
|
+
run_cmd = Covalence::DRY_RUN_CMD
|
31
|
+
end
|
32
|
+
|
33
|
+
if debug
|
34
|
+
return 0 unless HighLine.new.agree('Execute? [y/n]')
|
35
|
+
end
|
36
|
+
|
37
|
+
spawn_subprocess(ENV, run_cmd, {
|
38
|
+
stdin_io: stdin_io,
|
39
|
+
stdout_io: stdout_io,
|
40
|
+
stderr_io: stderr_io,
|
41
|
+
ignore_exitcode: ignore_exitcode
|
42
|
+
})
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def print_cmd_string(cmd_string)
|
47
|
+
Covalence::LOGGER.warn "---"
|
48
|
+
Covalence::LOGGER.warn cmd_string
|
49
|
+
end
|
50
|
+
|
51
|
+
def spawn_subprocess(env, run_cmd,
|
52
|
+
stdin_io: STDIN,
|
53
|
+
stdout_io: STDOUT,
|
54
|
+
stderr_io: STDERR,
|
55
|
+
ignore_exitcode: false)
|
56
|
+
Signal.trap("INT") {} #Disable parent process from exiting, orphaning the child fork below
|
57
|
+
wait_thread = nil
|
58
|
+
|
59
|
+
Open3.popen3(env, run_cmd) do |stdin, stdout, stderr, wait_thr|
|
60
|
+
mappings = { stdin_io => stdin, stdout => stdout_io, stderr => stderr_io }
|
61
|
+
wait_thread = wait_thr
|
62
|
+
|
63
|
+
Signal.trap("INT") {
|
64
|
+
Process.kill("INT", wait_thr.pid)
|
65
|
+
Process.wait(wait_thr.pid, Process::WNOHANG)
|
66
|
+
|
67
|
+
exit(wait_thr.value.exitstatus)
|
68
|
+
} # let SIGINT drop into the child process
|
69
|
+
|
70
|
+
handle_io_streams(mappings, stdin_io)
|
71
|
+
end
|
72
|
+
|
73
|
+
Signal.trap("INT") { exit } #Restore parent SIGINT
|
74
|
+
|
75
|
+
return 0 if ignore_exitcode
|
76
|
+
exit(wait_thread.value.exitstatus) unless wait_thread.value.success?
|
77
|
+
return wait_thread.value.exitstatus
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_io_streams(mappings, stdin_io)
|
81
|
+
inputs = mappings.keys
|
82
|
+
streams_ready_for_eof_check = []
|
83
|
+
|
84
|
+
until inputs.empty? || (inputs.size == 1 && inputs.first == stdin_io) do
|
85
|
+
|
86
|
+
readable_inputs, _ = IO.select(inputs, [], [])
|
87
|
+
streams_ready_for_eof_check = readable_inputs
|
88
|
+
|
89
|
+
streams_ready_for_eof_check.select(&:eof).each do |src|
|
90
|
+
Covalence::LOGGER.debug "Stopping redirection from an IO in EOF: " + src.inspect
|
91
|
+
# `select`ing an IO which has reached EOF blocks forever.
|
92
|
+
# So you have to delete such IO from the array of IOs to `select`.
|
93
|
+
inputs.delete src
|
94
|
+
|
95
|
+
# You must close the child process' STDIN immeditely after the parent's STDIN reached EOF,
|
96
|
+
# or some kinds of child processes never exit.
|
97
|
+
# e.g.) echo foobar | joumae run -- cat
|
98
|
+
# After the `echo` finished outputting `foobar`, you have to tell `cat` about that or `cat` will wait for more inputs forever.
|
99
|
+
mappings[src].close if src == stdin_io
|
100
|
+
end
|
101
|
+
|
102
|
+
break if inputs.empty? || (inputs.size == 1 && inputs.first == stdin_io)
|
103
|
+
|
104
|
+
readable_inputs.each do |input|
|
105
|
+
begin
|
106
|
+
data = input.read_nonblock(1024)
|
107
|
+
output = mappings[input]
|
108
|
+
output.write(data)
|
109
|
+
output.flush
|
110
|
+
rescue EOFError => e
|
111
|
+
Covalence::LOGGER.debug "Reached EOF: #{e}"
|
112
|
+
inputs.delete input
|
113
|
+
rescue Errno::EPIPE => e
|
114
|
+
Covalence::LOGGER.debug "Handled error: #{e}: io: #{input.inspect}"
|
115
|
+
inputs.delete input
|
116
|
+
end
|
117
|
+
end #readable_inputs
|
118
|
+
end #until inputs.empty?
|
119
|
+
end #handle_io_streams
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
---
|
2
|
+
version: Terraform v0.10.6
|
3
|
+
commands:
|
4
|
+
apply:
|
5
|
+
#console:
|
6
|
+
destroy:
|
7
|
+
#env:
|
8
|
+
#list:
|
9
|
+
#select:
|
10
|
+
#new:
|
11
|
+
#delete:
|
12
|
+
fmt:
|
13
|
+
#force-unlock
|
14
|
+
get:
|
15
|
+
graph:
|
16
|
+
#import
|
17
|
+
#init:
|
18
|
+
#output:
|
19
|
+
plan:
|
20
|
+
providers:
|
21
|
+
push:
|
22
|
+
refresh:
|
23
|
+
show:
|
24
|
+
state:
|
25
|
+
#list:
|
26
|
+
#mv:
|
27
|
+
#pull:
|
28
|
+
#push:
|
29
|
+
#rm:
|
30
|
+
#show:
|
31
|
+
#taint:
|
32
|
+
validate:
|
33
|
+
#untaint:
|
34
|
+
workspace:
|
35
|
+
list:
|
36
|
+
select:
|
37
|
+
new:
|
38
|
+
delete:
|
39
|
+
version:
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
require 'open3'
|
6
|
+
require 'semantic'
|
7
|
+
|
8
|
+
require_relative '../../../covalence'
|
9
|
+
require_relative 'popen_wrapper'
|
10
|
+
|
11
|
+
module Covalence
|
12
|
+
class TerraformCli
|
13
|
+
def self.require_init()
|
14
|
+
if Semantic::Version.new(Covalence::TERRAFORM_VERSION) < Semantic::Version.new("0.9.0")
|
15
|
+
raise "Terraform v0.9.0 or newer required"
|
16
|
+
else
|
17
|
+
cmds_yml = File.expand_path("terraform.yml", __dir__)
|
18
|
+
end
|
19
|
+
init_terraform_cmds(cmds_yml)
|
20
|
+
end
|
21
|
+
|
22
|
+
# :reek:BooleanParameter
|
23
|
+
def self.terraform_clean(path, dry_run: false, verbose: true)
|
24
|
+
# standard run shouldn't need this since it does a chdir on a temp dir anyway
|
25
|
+
# something about cln_cmd when working with docker images
|
26
|
+
targets = [ File.join(File.expand_path(path), ".terraform") ] +
|
27
|
+
Dir.glob(File.join(File.expand_path(path), "*.tfstate*"))
|
28
|
+
|
29
|
+
FileUtils.rm_rf(targets, {
|
30
|
+
noop: dry_run,
|
31
|
+
verbose: verbose,
|
32
|
+
secure: true,
|
33
|
+
})
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.terraform_check_style(path)
|
37
|
+
output, status = Open3.capture2e(ENV, Covalence::TERRAFORM_CMD, "fmt", "-write=false", path)
|
38
|
+
return false unless status.success?
|
39
|
+
output = output.split("\n")
|
40
|
+
(output.size == 0)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.terraform_init(path='', args: '', ignore_exitcode: false)
|
44
|
+
if ENV['TF_PLUGIN_LOCAL'] == 'true'
|
45
|
+
cmd = [Covalence::TERRAFORM_CMD, "init", "-get=false", "-input=false", "-plugin-dir=#{Covalence::TERRAFORM_PLUGIN_CACHE}"]
|
46
|
+
else
|
47
|
+
cmd = [Covalence::TERRAFORM_CMD, "init", "-get=false", "-input=false"]
|
48
|
+
end
|
49
|
+
|
50
|
+
output = PopenWrapper.run(
|
51
|
+
cmd,
|
52
|
+
path,
|
53
|
+
args,
|
54
|
+
ignore_exitcode: ignore_exitcode)
|
55
|
+
(output == 0)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.terraform_workspace(workspace, path='', args: '', ignore_exitcode: false)
|
59
|
+
cmd = [Covalence::TERRAFORM_CMD, "workspace", "new", workspace]
|
60
|
+
|
61
|
+
output = PopenWrapper.run(
|
62
|
+
cmd,
|
63
|
+
path,
|
64
|
+
args,
|
65
|
+
ignore_exitcode: ignore_exitcode)
|
66
|
+
|
67
|
+
(output == 0)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.terraform_output(output_var, args: '')
|
71
|
+
raise "TODO: implement me"
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.terraform_taint(resource_name, args: '')
|
75
|
+
raise "TODO: implement me"
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.terraform_untaint(resource_name, args: '')
|
79
|
+
raise "TODO: implement me"
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
class << self
|
84
|
+
private
|
85
|
+
|
86
|
+
# The only args that should be automated are ones that only expect some DIR/PATH as it's only
|
87
|
+
# required arg, most other things need a little bit more manual definition.
|
88
|
+
def init_terraform_cmds(file)
|
89
|
+
definition = YAML.load_file(file)
|
90
|
+
|
91
|
+
definition['commands'].each do |cmd, sub_hash|
|
92
|
+
if sub_hash.blank?
|
93
|
+
terraform_cmd = "terraform_#{cmd}"
|
94
|
+
|
95
|
+
next if respond_to?(terraform_cmd.to_sym)
|
96
|
+
define_singleton_method(terraform_cmd) do |path=Dir.pwd(), args: ''|
|
97
|
+
output = PopenWrapper.run([Covalence::TERRAFORM_CMD, cmd], path, args)
|
98
|
+
(output == 0)
|
99
|
+
end
|
100
|
+
elsif sub_hash.is_a?(Hash)
|
101
|
+
sub_hash.keys.each do |sub_command|
|
102
|
+
terraform_cmd = "terraform_#{cmd}_#{sub_command}"
|
103
|
+
|
104
|
+
next if respond_to?(terraform_cmd.to_sym)
|
105
|
+
define_singleton_method(terraform_cmd) do |path=Dir.pwd(), args: ''|
|
106
|
+
output = PopenWrapper.run([Covalence::TERRAFORM_CMD, cmd, sub_command], path, args)
|
107
|
+
(output == 0)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
else
|
111
|
+
raise "Invalid yml context"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end #init_terraform_cmds
|
115
|
+
end #private
|
116
|
+
end #TerraformCli
|
117
|
+
end
|
118
|
+
|
119
|
+
Covalence::TerraformCli.require_init
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative '../../../covalence'
|
2
|
+
require 'yaml'
|
3
|
+
require 'hiera'
|
4
|
+
|
5
|
+
module Covalence
|
6
|
+
module HieraDB
|
7
|
+
# TODO: maybe HieraWrapper
|
8
|
+
# :reek:DataClump
|
9
|
+
class Client
|
10
|
+
attr_reader :scope
|
11
|
+
|
12
|
+
def initialize(config, scope = {})
|
13
|
+
@config = config
|
14
|
+
@scope = scope
|
15
|
+
|
16
|
+
begin
|
17
|
+
@client = Hiera.new(:config => config)
|
18
|
+
rescue RuntimeError => e
|
19
|
+
Covalence::LOGGER.error e.message
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize_scope(scope)
|
25
|
+
self.class.new(@config, scope)
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_scope(env, stack)
|
29
|
+
@scope = {
|
30
|
+
"environment" => env,
|
31
|
+
"stack" => stack
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def lookup(key, default = nil, scope = @scope)
|
36
|
+
@client.lookup(key, default, scope)
|
37
|
+
end
|
38
|
+
|
39
|
+
def hash_lookup(key, default = nil, scope = @scope)
|
40
|
+
# https://github.com/puppetlabs/hiera/blob/d7ed74f4eec8f4fb1aa84cd0e158a595f86debd4/lib/hiera/backend.rb#L241
|
41
|
+
# def lookup(key, default, scope, order_override, resolution_type, context = {:recurse_guard => nil})
|
42
|
+
@client.lookup(key, default, scope, nil, :hash)
|
43
|
+
end
|
44
|
+
|
45
|
+
def array_lookup(key, default = nil, scope = @scope)
|
46
|
+
@client.lookup(key, default, scope, nil, :array)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'virtus'
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
module Covalence
|
6
|
+
# Maybe just call this targets
|
7
|
+
class Context
|
8
|
+
include Virtus.model
|
9
|
+
include ActiveModel::Validations
|
10
|
+
|
11
|
+
attribute :name, String, default: ''
|
12
|
+
attribute :values, Array, default: []
|
13
|
+
|
14
|
+
validates! :name, format: {
|
15
|
+
without: /(\s+|,)/,
|
16
|
+
message: "Context %{attribute}: \"%{value}\" cannot contain spaces or commas"
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize(attributes = {}, *args)
|
20
|
+
super
|
21
|
+
self.valid?
|
22
|
+
end
|
23
|
+
|
24
|
+
def namespace
|
25
|
+
return "" if name.blank?
|
26
|
+
"#{name}:"
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_command_options
|
30
|
+
values.map { |value| "-target=\"#{value}\"" }
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_packer_command_options
|
34
|
+
return "" if values.blank?
|
35
|
+
"-only=#{values.join(',')}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|