diff --git a/bin/cuffdown b/bin/cuffdown index 57b93e5..20abe50 100755 --- a/bin/cuffdown +++ b/bin/cuffdown @@ -1,55 +1,5 @@ #!/usr/bin/env ruby -require 'cuffsert/metadata' -require 'cuffsert/rxcfclient' -require 'yaml' +require 'cuffdown/main' -module CuffDown - def self.parameters(stack) - (stack[:parameters] || []).map do |param| - { - 'Name' => param[:parameter_key], - 'Value' => param[:parameter_value], - } - end - end - - def self.tags(stack) - (stack[:tags] || []).map do |param| - { - 'Name' => param[:key], - 'Value' => param[:value], - } - end - end - - def self.dump(name, params, tags, output) - result = { - 'Format' => 'v1', - 'Suffix' => name, - 'Parameters' => params, - 'Tags' => tags, - } - YAML.dump(result, output) - end - - def self.run(args) - meta = CuffSert::StackConfig.new - meta.stackname = args[0] - client = CuffSert::RxCFClient.new - stack = client.find_stack_blocking(meta) - unless stack - STDERR.puts "No such stack #{meta.stackname}" - exit(1) - end - stack = stack.to_h - self.dump( - stack[:stack_name], - self.parameters(stack), - self.tags(stack), - STDOUT - ) - end -end - -CuffDown.run(ARGV) +CuffDown.run(ARGV, STDOUT) diff --git a/lib/cuffbase.rb b/lib/cuffbase.rb index 7a688fa..e564ce6 100644 --- a/lib/cuffbase.rb +++ b/lib/cuffbase.rb @@ -1,6 +1,12 @@ require 'yaml' module CuffBase + def self.shared_cli_args(opts, args) + opts.on('--region=aws_region', 'AWS region, overrides env variable AWS_REGION') do |region| + args[:aws_region] = region + end + end + def self.empty_from_template(io) self.template_parameters(io) {|_| nil } end diff --git a/lib/cuffdown/main.rb b/lib/cuffdown/main.rb new file mode 100644 index 0000000..a1dc363 --- /dev/null +++ b/lib/cuffdown/main.rb @@ -0,0 +1,67 @@ +require 'cuffbase' +require 'cuffsert/metadata' +require 'cuffsert/rxcfclient' +require 'optparse' +require 'yaml' + +module CuffDown + def self.parse_cli_args(argv) + args = {} + parser = OptionParser.new do |opts| + opts.banner = 'Output CuffSert-formatted metadata from an existing stack.' + opts.separator('') + opts.separator('Usage: cuffdown stack-name') + CuffBase.shared_cli_args(opts, args) + end + stackname, _ = parser.parse(argv) + args[:stackname] = stackname + args + end + + def self.parameters(stack) + (stack[:parameters] || []).map do |param| + { + 'Name' => param[:parameter_key], + 'Value' => param[:parameter_value], + } + end + end + + def self.tags(stack) + (stack[:tags] || []).map do |param| + { + 'Name' => param[:key], + 'Value' => param[:value], + } + end + end + + def self.dump(name, params, tags, output) + result = { + 'Format' => 'v1', + 'Suffix' => name, + 'Parameters' => params, + 'Tags' => tags, + } + YAML.dump(result, output) + end + + def self.run(argv, output) + cli_args = self.parse_cli_args(argv) + meta = CuffSert::StackConfig.new + meta.stackname = cli_args[:stackname] + client = CuffSert::RxCFClient.new(cli_args[:aws_region]) + stack = client.find_stack_blocking(meta) + unless stack + STDERR.puts "No such stack #{meta.stackname}" + exit(1) + end + stack = stack.to_h + self.dump( + stack[:stack_name], + self.parameters(stack), + self.tags(stack), + output + ) + end +end diff --git a/lib/cuffsert/cli_args.rb b/lib/cuffsert/cli_args.rb index 939f439..e20cfcd 100644 --- a/lib/cuffsert/cli_args.rb +++ b/lib/cuffsert/cli_args.rb @@ -1,4 +1,5 @@ require 'optparse' +require 'cuffbase' require 'cuffsert/version' module CuffSert @@ -23,6 +24,9 @@ def self.parse_cli_args(argv) opts.separator(' cuffsert --name {--parameter Name=Value | --tag Name=Value}... [stack.json]') opts.separator(' cuffsert --metadata --selector stack.json') opts.separator(' cuffsert --metadata --selector {--parameter Name=Value | --tag Name=Value}... [stack.json]') + + CuffBase.shared_cli_args(opts, args) + opts.on('--metadata path', '-m path', 'Yaml file to read stack metadata from') do |path| path = '/dev/stdin' if path == '-' unless File.exist?(path) @@ -64,10 +68,6 @@ def self.parse_cli_args(argv) args[:overrides][:tags][key] = val end - opts.on('--region=aws_region', 'AWS region, overrides env variable AWS_REGION') do |region| - args[:aws_region] = region - end - opts.on('--s3-upload-prefix=prefix', 'Templates > 51200 bytes are uploaded here. Format: s3://bucket-name/[pre/fix]') do |prefix| unless prefix.start_with?('s3://') raise "Upload prefix #{prefix} must start with s3://" diff --git a/spec/cuffdown/main_spec.rb b/spec/cuffdown/main_spec.rb new file mode 100644 index 0000000..cb01813 --- /dev/null +++ b/spec/cuffdown/main_spec.rb @@ -0,0 +1,38 @@ +require 'cuffdown/main' +require 'spec_helpers' + +describe CuffDown do + include_context 'stack states' + + describe '.run' do + let(:cli_args) { ['some-stack'] } + let(:output) { StringIO.new } + let :stack_complete do + super().merge( + :parameters => [ + {:parameter_key => 'p1', :parameter_value => 'v1'} + ] + ) + end + + let(:cfmock) { double(:cfmock) } + + before do + allow(Aws::CloudFormation::Client).to receive(:new).and_return(cfmock) + allow(cfmock).to receive(:describe_stacks).and_return(stack_complete_describe) + CuffDown.run(cli_args, output) + end + + it 'outputs parameter values for the stack' do + expect(output.string).to match(/Name: p1.*Value: v1/m) + end + + context 'when invoked with an explicit region' do + let(:cli_args) { ['--region', 'rp-north-1', 'stack-name'] } + + it 'passes region to the ClodFormation client' do + expect(Aws::CloudFormation::Client).to have_received(:new).with(hash_including(:region => 'rp-north-1')) + end + end + end +end