covalence 0.0.1 → 0.7.9.rc1
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 +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
|