dh-proteus 0.1.3
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 +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +80 -0
- data/README.md +414 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/proteus +5 -0
- data/bin/proteus_testing +8 -0
- data/bin/setup +8 -0
- data/build.sh +28 -0
- data/dh-proteus.gemspec +50 -0
- data/lib/core_ext/hash.rb +14 -0
- data/lib/proteus.rb +9 -0
- data/lib/proteus/app.rb +86 -0
- data/lib/proteus/backend/backend.rb +41 -0
- data/lib/proteus/commands/apply.rb +53 -0
- data/lib/proteus/commands/clean.rb +22 -0
- data/lib/proteus/commands/destroy.rb +51 -0
- data/lib/proteus/commands/graph.rb +22 -0
- data/lib/proteus/commands/import.rb +75 -0
- data/lib/proteus/commands/move.rb +28 -0
- data/lib/proteus/commands/output.rb +29 -0
- data/lib/proteus/commands/plan.rb +55 -0
- data/lib/proteus/commands/remove.rb +71 -0
- data/lib/proteus/commands/render.rb +36 -0
- data/lib/proteus/commands/taint.rb +35 -0
- data/lib/proteus/common.rb +72 -0
- data/lib/proteus/config/config.rb +47 -0
- data/lib/proteus/context_management/context.rb +31 -0
- data/lib/proteus/context_management/helpers.rb +14 -0
- data/lib/proteus/generate.rb +18 -0
- data/lib/proteus/generators/context.rb +57 -0
- data/lib/proteus/generators/environment.rb +42 -0
- data/lib/proteus/generators/init.rb +40 -0
- data/lib/proteus/generators/module.rb +69 -0
- data/lib/proteus/generators/templates/config/config.yaml.erb +22 -0
- data/lib/proteus/generators/templates/context/main.tf.erb +7 -0
- data/lib/proteus/generators/templates/context/variables.tf.erb +3 -0
- data/lib/proteus/generators/templates/environment/terraform.tfvars.erb +6 -0
- data/lib/proteus/generators/templates/module/io.tf.erb +1 -0
- data/lib/proteus/generators/templates/module/module.tf.erb +1 -0
- data/lib/proteus/generators/templates/module/validator.rb.erb +9 -0
- data/lib/proteus/global_commands/validate.rb +45 -0
- data/lib/proteus/helpers.rb +91 -0
- data/lib/proteus/helpers/path_helpers.rb +90 -0
- data/lib/proteus/helpers/string_helpers.rb +13 -0
- data/lib/proteus/init.rb +16 -0
- data/lib/proteus/modules/manager.rb +53 -0
- data/lib/proteus/modules/terraform_module.rb +184 -0
- data/lib/proteus/templates/partial.rb +123 -0
- data/lib/proteus/templates/template_binding.rb +62 -0
- data/lib/proteus/validators/base_validator.rb +24 -0
- data/lib/proteus/validators/validation_dsl.rb +172 -0
- data/lib/proteus/validators/validation_error.rb +9 -0
- data/lib/proteus/validators/validation_helpers.rb +9 -0
- data/lib/proteus/version.rb +7 -0
- metadata +260 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "proteus"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/proteus
ADDED
data/bin/proteus_testing
ADDED
data/bin/setup
ADDED
data/build.sh
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -eo pipefail
|
4
|
+
|
5
|
+
AWS_PROFILE=logistics-production
|
6
|
+
BUCKET=production-eu-gems
|
7
|
+
NEXUS_REPOSITORY=https://nexus.usehurrier.com/repository/proteus/
|
8
|
+
VERSION=$(ruby lib/proteus/version.rb)
|
9
|
+
|
10
|
+
|
11
|
+
if ! gem list --local | grep nexus; then
|
12
|
+
echo Please install the nexus gem in order to push to the gem repository: \"gem install nexus\"
|
13
|
+
exit 1
|
14
|
+
fi
|
15
|
+
|
16
|
+
echo Building version $VERSION
|
17
|
+
|
18
|
+
mkdir -p ./release/gems
|
19
|
+
|
20
|
+
rake build
|
21
|
+
|
22
|
+
cp ./pkg/*.gem ./release/gems/
|
23
|
+
|
24
|
+
gem generate_index --directory ./release/
|
25
|
+
|
26
|
+
aws --profile $AWS_PROFILE s3 sync ./release s3://$BUCKET
|
27
|
+
|
28
|
+
gem nexus release/gems/dh-proteus-$VERSION.gem --url $NEXUS_REPOSITORY
|
data/dh-proteus.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "proteus/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dh-proteus"
|
8
|
+
spec.version = Proteus::VERSION
|
9
|
+
spec.authors = ["Simon Albrecht"]
|
10
|
+
spec.email = ["simon.albrecht@deliveryhero.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Proteus is a Terraform wrapper application.}
|
13
|
+
#spec.description = %q{Write a longer description or delete this line.}
|
14
|
+
spec.homepage = "https://github.com/deliveryhero/proteus"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
20
|
+
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
22
|
+
spec.metadata["source_code_uri"] = "https://github.com/deliveryhero/proteus"
|
23
|
+
spec.metadata["changelog_uri"] = "https://github.com/deliveryhero/proteus/CHANGELOG"
|
24
|
+
else
|
25
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
26
|
+
"public gem pushes."
|
27
|
+
end
|
28
|
+
|
29
|
+
# Specify which files should be added to the gem when it is released.
|
30
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
31
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
32
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
|
+
end
|
34
|
+
spec.bindir = "bin"
|
35
|
+
spec.executables = ['proteus']
|
36
|
+
spec.require_paths = ["lib"]
|
37
|
+
|
38
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
39
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
40
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
41
|
+
spec.add_development_dependency "pry"
|
42
|
+
|
43
|
+
spec.add_runtime_dependency 'activesupport', '~> 5.1.1'
|
44
|
+
spec.add_runtime_dependency 'thor', '~> 0.20.0'
|
45
|
+
spec.add_runtime_dependency 'erubis', '~> 2.7.0'
|
46
|
+
spec.add_runtime_dependency 'hcl-checker', '~> 1.0.5'
|
47
|
+
spec.add_runtime_dependency 'aws-sdk-rds', '~> 1.11.0'
|
48
|
+
spec.add_runtime_dependency 'aws-sdk-route53', '~> 1.7.0'
|
49
|
+
spec.add_runtime_dependency 'aws-sdk-elasticsearchservice', '~> 1.4.0'
|
50
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class ::Hash
|
2
|
+
def deep_merge(second)
|
3
|
+
merger = proc do |_, v1, v2|
|
4
|
+
if v1.is_a?(Hash) && v2.is_a?(Hash)
|
5
|
+
v1.merge(v2, &merger)
|
6
|
+
elsif v1.is_a?(Array) && v2.is_a?(Array)
|
7
|
+
v1 | v2
|
8
|
+
else
|
9
|
+
[:undefined, nil, :nil].include?(v2) ? v1 : v2
|
10
|
+
end
|
11
|
+
end
|
12
|
+
merge(second.to_h, &merger)
|
13
|
+
end
|
14
|
+
end
|
data/lib/proteus.rb
ADDED
data/lib/proteus/app.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'proteus/common'
|
2
|
+
require 'proteus/generate'
|
3
|
+
require 'proteus/init'
|
4
|
+
require 'proteus/global_commands/validate'
|
5
|
+
require 'proteus/context_management/context'
|
6
|
+
require 'proteus/context_management/helpers'
|
7
|
+
require 'proteus/templates/template_binding'
|
8
|
+
require 'proteus/templates/partial'
|
9
|
+
|
10
|
+
module Proteus
|
11
|
+
|
12
|
+
class Context < Thor; end
|
13
|
+
|
14
|
+
class App < Thor
|
15
|
+
extend Proteus::Helpers
|
16
|
+
extend Proteus::Helpers::StringHelpers
|
17
|
+
extend Proteus::ContextManagement::Helpers
|
18
|
+
|
19
|
+
class_option :dryrun, type: :boolean, default: false, aliases: '-d'
|
20
|
+
class_option :verbose, type: :boolean, default: false, aliases: '-v'
|
21
|
+
|
22
|
+
contexts.each do |context|
|
23
|
+
|
24
|
+
if context.name != 'default'
|
25
|
+
|
26
|
+
# generate class for subcommand within contexts subcommand
|
27
|
+
context_subcommand_class = Class.new(Thor)
|
28
|
+
context_subcommand_class_name = camel_case(context.name)
|
29
|
+
Object.const_set(context_subcommand_class_name, context_subcommand_class)
|
30
|
+
|
31
|
+
Proteus::Context.class_eval do
|
32
|
+
desc "#{context.name} SUBCOMMAND", "Manage the #{context.name} context."
|
33
|
+
subcommand(context.name, const_get(context_subcommand_class_name))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
mod_name = Object.const_set("Module#{camel_case(context.name)}", Module.new)
|
38
|
+
|
39
|
+
context.environments.each do |environment|
|
40
|
+
class_name = camel_case(environment)
|
41
|
+
|
42
|
+
klass = Class.new(Proteus::Common)
|
43
|
+
|
44
|
+
mod_name.const_set(class_name, klass)
|
45
|
+
|
46
|
+
klass.class_eval do
|
47
|
+
include Config
|
48
|
+
@context = context.name
|
49
|
+
@environment = environment
|
50
|
+
|
51
|
+
def self.context
|
52
|
+
@context
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.environment
|
56
|
+
@environment
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if context.name == 'default'
|
61
|
+
# attach subcommands for the standard environments directly
|
62
|
+
# eg.:
|
63
|
+
# ./proteus production_eu SUBCOMMAND
|
64
|
+
desc "#{environment} SUBCOMMAND", "Manage the #{environment} environment in context default"
|
65
|
+
subcommand(environment, mod_name.const_get(class_name))
|
66
|
+
else
|
67
|
+
const_get(context_subcommand_class_name).class_eval do
|
68
|
+
desc "#{environment} SUBCOMMAND", "Manage the #{environment} environment in context #{context.name}"
|
69
|
+
subcommand(environment, mod_name.const_get(class_name))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
desc 'generate SUBCOMMAND', 'Generators for modules and environments'
|
76
|
+
subcommand('generate', Proteus::Generate)
|
77
|
+
|
78
|
+
desc 'context SUBCOMMAND', 'Context subcommands'
|
79
|
+
subcommand('context', Context)
|
80
|
+
|
81
|
+
desc 'init', 'Initializes a new proteus root directory in the current working directory'
|
82
|
+
subcommand('init', Proteus::Init)
|
83
|
+
|
84
|
+
include Proteus::GlobalCommands::Validate
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'proteus/helpers/path_helpers'
|
2
|
+
|
3
|
+
module Proteus
|
4
|
+
module Backend
|
5
|
+
class Backend
|
6
|
+
include Proteus::Helpers::PathHelpers
|
7
|
+
include Thor::Shell
|
8
|
+
|
9
|
+
def initialize(config:, context:, environment:)
|
10
|
+
@config = config
|
11
|
+
@context = context
|
12
|
+
@environment = environment
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
File.open(File.join(context_path(@context), 'backend.tf'), 'w') do |file|
|
17
|
+
file.write(Erubis::Eruby.new(template).result(binding))
|
18
|
+
end
|
19
|
+
rescue StandardError => e
|
20
|
+
say 'Error rendering backend config.', :magenta
|
21
|
+
say e.message, :magenta
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def template
|
28
|
+
<<~TEMPLATE
|
29
|
+
terraform {
|
30
|
+
backend "s3" {
|
31
|
+
bucket = "<%= @config[:backend][:bucket][:name] %>"
|
32
|
+
key = "<%= @config[:backend][:key_prefix] %>#{@context}-#{@environment}.tfstate"
|
33
|
+
region = "<%= @config[:backend][:bucket][:region] %>"
|
34
|
+
profile = "<%= @config[:backend][:bucket][:profile]%>"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
TEMPLATE
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Apply
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc 'apply', 'Applies the current terraform code'
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Applies the current terraform code
|
10
|
+
|
11
|
+
With --limit option, Terraform will only apply changes for the the specified resources
|
12
|
+
LONGDESC
|
13
|
+
option :limit, type: :array, aliases: "-l", required: false, default: nil
|
14
|
+
def apply
|
15
|
+
init(verbose: parent_options[:verbose])
|
16
|
+
|
17
|
+
confirm question: "Do you really want to run 'terraform apply' on environment '#{environment}' in context '#{context}'?", color: :on_red, exit_code: 0 do
|
18
|
+
|
19
|
+
|
20
|
+
if !options[:dryrun]
|
21
|
+
slack_notification(
|
22
|
+
context: context,
|
23
|
+
environment: environment,
|
24
|
+
message: 'is running terraform apply. Wait for it.'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
apply_command = <<~APPLY_COMMAND
|
29
|
+
cd #{context_path(context)} && \
|
30
|
+
terraform apply \
|
31
|
+
-input=true \
|
32
|
+
-refresh=true \
|
33
|
+
#{limit(options[:limit])} \
|
34
|
+
#{plan_file(context, environment)}
|
35
|
+
APPLY_COMMAND
|
36
|
+
|
37
|
+
syscall apply_command.squeeze(' '), dryrun: dryrun
|
38
|
+
|
39
|
+
if !options[:dryrun]
|
40
|
+
slack_notification(
|
41
|
+
context: context,
|
42
|
+
environment: environment,
|
43
|
+
message: 'applied a new version of me!'
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'proteus/modules/manager'
|
2
|
+
|
3
|
+
module Proteus
|
4
|
+
module Commands
|
5
|
+
module Clean
|
6
|
+
def self.included(thor_class)
|
7
|
+
thor_class.class_eval do |klass|
|
8
|
+
|
9
|
+
desc "clean", "Deletes rendered module manifests"
|
10
|
+
long_desc <<-LONGDESC
|
11
|
+
Deletes rendered module manifests without running Terraform
|
12
|
+
LONGDESC
|
13
|
+
def clean
|
14
|
+
module_manager = Proteus::Modules::Manager.new(context: context, environment: environment)
|
15
|
+
module_manager.clean
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Destroy
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "destroy", "Destroys AWS resources."
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Destroys AWS resources.
|
10
|
+
|
11
|
+
With --limit option, Terraform will only destroy the specified resources
|
12
|
+
If --limit does not get passed as an argument, terraform will destroy all of the AWS assets in the state.
|
13
|
+
LONGDESC
|
14
|
+
option :limit, type: :array, aliases: "-l", required: false
|
15
|
+
def destroy
|
16
|
+
init(verbose: parent_options[:verbose])
|
17
|
+
|
18
|
+
destroy_command = <<~DESTROY_COMMAND
|
19
|
+
cd #{context_path(context)} && \
|
20
|
+
terraform destroy \
|
21
|
+
-var-file=#{var_file(context, environment)} \
|
22
|
+
#{aws_profile} \
|
23
|
+
##{limit(options[:limit])}
|
24
|
+
DESTROY_COMMAND
|
25
|
+
|
26
|
+
plan_destroy_command = <<~PLAN_DESTROY_COMMAND
|
27
|
+
cd #{context_path(context)} && \
|
28
|
+
terraform plan \
|
29
|
+
-destroy \
|
30
|
+
-out=#{plan_file(context, environment)} \
|
31
|
+
-var-file=#{var_file(context, environment)} \
|
32
|
+
#{aws_profile} \
|
33
|
+
##{limit(options[:limit])}
|
34
|
+
PLAN_DESTROY_COMMAND
|
35
|
+
|
36
|
+
|
37
|
+
if dryrun
|
38
|
+
puts destroy_command.squeeze(' ')
|
39
|
+
else
|
40
|
+
syscall plan_destroy_command.squeeze(' ')
|
41
|
+
confirm question: "Do you really want to run 'terraform destroy' on environment '#{environment}'?", color: :on_red, exit_code: 0 do
|
42
|
+
exec destroy_command.squeeze(' ')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Graph
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "graph", "Creates a graph showing the resources and their relations."
|
8
|
+
def graph
|
9
|
+
say "Generating graph. This might take a while"
|
10
|
+
graph_command = <<~GRAPH_COMMAND
|
11
|
+
cd #{context_path(context)} \
|
12
|
+
&& terraform graph -draw-cycles -module-depth=1 | dot -Tpng > graph.png \
|
13
|
+
&& open graph.png
|
14
|
+
GRAPH_COMMAND
|
15
|
+
`#{graph_command}`
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Import
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "import", "Imports an existing resource into the terraform state"
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Imports existing resources into the terraform state
|
10
|
+
|
11
|
+
--address Terraform internal address of the resource
|
12
|
+
|
13
|
+
--bulk Enables bulk import mode
|
14
|
+
|
15
|
+
--resource The resource to import into the terraform state
|
16
|
+
|
17
|
+
--resources_file File containing resource addresses and identifiers
|
18
|
+
LONGDESC
|
19
|
+
option :address, type: :string, aliases: "-a", default: nil
|
20
|
+
option :bulk, type: :boolean, aliases: "-b", required: false, default: false
|
21
|
+
option :resource, type: :string, aliases: "-r", default: nil
|
22
|
+
option :resources_file, type: :string, aliases: "-f", required: false, default: nil
|
23
|
+
def import
|
24
|
+
if options[:bulk]
|
25
|
+
if !options[:resources_file]
|
26
|
+
say "Supply a file containing resource identifiers and Terraform addresses for bulk operations", :red
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
else
|
30
|
+
if !(options[:address] || !options[:resource])
|
31
|
+
say "You need to supply a resource address and a resource.", :red
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
init(verbose: parent_options[:verbose])
|
37
|
+
|
38
|
+
confirm question: "Do you really want to run 'terraform import' in context '(#{context}, #{environment})'?", color: :on_red, exit_code: 0 do
|
39
|
+
|
40
|
+
import_command = <<~IMPORT_COMMAND
|
41
|
+
cd #{context_path(context)} && \
|
42
|
+
terraform import \
|
43
|
+
-var-file=#{var_file(context, environment)} \
|
44
|
+
#{aws_profile} \
|
45
|
+
%{address} \
|
46
|
+
%{resource}
|
47
|
+
IMPORT_COMMAND
|
48
|
+
|
49
|
+
if options[:bulk]
|
50
|
+
if File.file?(options[:resources_file])
|
51
|
+
File.open(options[:resources_file], "r") do |file|
|
52
|
+
resource_count = File.foreach(file).inject(0) {|count, line| count + 1}
|
53
|
+
index = 1
|
54
|
+
file.each_line do |line|
|
55
|
+
say "Processing resource #{index}/#{resource_count}", :green
|
56
|
+
resource = line.chomp.split(" = ")
|
57
|
+
syscall (import_command % { address: resource[0], resource: resource[1] }).squeeze(' ')
|
58
|
+
index += 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
else
|
62
|
+
say "File #{options[:resources_file]} does not exist.", :red
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
else
|
66
|
+
syscall (import_command % { address: options[:address], resource: options[:resource] })
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|