bbc-cosmos-tools 0.5.2 → 0.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1b3b62925024f13d3a5b7faae3e72eefa93f6dc
4
- data.tar.gz: ffdad2cc037fcf0f7a80e0621a5e112866caf6bc
3
+ metadata.gz: 682aa789050e6b06223d07a9da105df6abdc966d
4
+ data.tar.gz: 2256eaa0c3a4c060fb26fdaa8fa637796602723c
5
5
  SHA512:
6
- metadata.gz: 70676ba07d90b32de1b3d0e780f5bc505728c9e2668719b55e9aed3a61298b3f65077a907ff46ba8eb4c72003be0bb58671a836cf5439728e823fc60473f9b2f
7
- data.tar.gz: cbb8ec4259306edc4f0269acca9b06173ccfb0c16d310057d19e9c512baf26c7729bbab071d2026fb4b0712c3e681fb7c35654861bed976eee37fa90c9728291
6
+ metadata.gz: 8cb7f2f87c0d45e710caa0fd6f85b887410927bd37570894cfbee4eb88d7bd8df4c2976a14bd2a5e05bb505b52e95a950ec5ea7192b55e9fdf81c22643264188
7
+ data.tar.gz: 9b4b400d1c6cd2da4bb81c22518dfb5ee8e80217a9b7f3879bbec47cd50edbce62bf9a0478c87131e4f60b37b9318b68150f1170d101565815b45a78ffa021cc
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in bbc-cosmos-config.gemspec
4
4
  gemspec
@@ -8,12 +8,12 @@ Gem::Specification.new do |spec|
8
8
  spec.version = BBC::Cosmos::Tools::VERSION
9
9
  spec.authors = ["Steven Jack"]
10
10
  spec.email = ["stevenmajack@gmail.com"]
11
- spec.description = %q{Tool for interacting with BBC Cosmos platform}
12
- spec.summary = %q{Tool for pusing config to cosmos and controlling cloudformation templates}
11
+ spec.description = "Tool for interacting with BBC Cosmos platform"
12
+ spec.summary = "Tool for pusing config to cosmos and controlling cloudformation templates"
13
13
  spec.homepage = ""
14
14
  spec.license = "MIT"
15
15
 
16
- spec.files = `git ls-files`.split($/)
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
17
  spec.executables << "bbc-cosmos-tools"
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
@@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency "thor"
25
25
  spec.add_runtime_dependency "faraday"
26
26
  spec.add_runtime_dependency "cfndsl"
27
- spec.add_runtime_dependency "aws-sdk", "~> 1"
27
+ spec.add_runtime_dependency "aws-sdk", "~> 1.63.0"
28
+ spec.add_runtime_dependency "highline"
28
29
  end
data/bin/bbc-cosmos-tools CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'pathname'
3
+ require "pathname"
4
4
 
5
5
  root = Pathname.new(__FILE__).dirname.parent
6
- lib_path = (root + 'lib').realdirpath
6
+ lib_path = (root + "lib").realdirpath
7
7
 
8
8
  $LOAD_PATH.unshift(lib_path)
9
9
 
10
- require 'bbc/cosmos/tools/app'
10
+ require "bbc/cosmos/tools/app"
11
11
 
12
12
  include BBC::Cosmos::Tools
13
13
 
@@ -1,18 +1,17 @@
1
- require 'pathname'
2
- require 'faraday'
3
- require 'openssl'
1
+ require "pathname"
2
+ require "faraday"
3
+ require "openssl"
4
4
 
5
5
  module BBC
6
6
  module Cosmos
7
7
  module Tools
8
8
  class API
9
-
10
9
  def initialize(host, key_path)
11
10
  @connection = Faraday.new host, :ssl => {
12
11
  :client_key => client_key(key_path),
13
12
  :client_cert => client_cert(key_path),
14
13
  :verify => false,
15
- :version => 'TLSv1'
14
+ :version => "TLSv1"
16
15
  }
17
16
  end
18
17
 
@@ -28,12 +27,12 @@ module BBC
28
27
  @connection.get path
29
28
  end
30
29
 
31
- def put(path, payload, content_type = 'application/json')
30
+ def put(path, payload, content_type = "application/json")
32
31
  content_type_from content_type
33
32
  @connection.put path, payload
34
33
  end
35
34
 
36
- def post(path, payload, content_type = 'application/json')
35
+ def post(path, payload, content_type = "application/json")
37
36
  content_type_from content_type
38
37
  @connection.post path, payload
39
38
  end
@@ -41,7 +40,6 @@ module BBC
41
40
  def content_type_from(type)
42
41
  @connection.headers[:content_type] = type
43
42
  end
44
-
45
43
  end
46
44
  end
47
45
  end
@@ -1,8 +1,8 @@
1
1
  require "thor"
2
- require 'bbc/cosmos/tools/commands/config'
3
- require 'bbc/cosmos/tools/commands/release'
4
- require 'bbc/cosmos/tools/commands/stack'
5
- require 'bbc/cosmos/tools/commands/component'
2
+ require "bbc/cosmos/tools/commands/config"
3
+ require "bbc/cosmos/tools/commands/release"
4
+ require "bbc/cosmos/tools/commands/stack"
5
+ require "bbc/cosmos/tools/commands/component"
6
6
 
7
7
  module BBC
8
8
  module Cosmos
@@ -1,20 +1,19 @@
1
- require 'cfndsl'
2
- require 'json'
1
+ require "cfndsl"
2
+ require "json"
3
3
 
4
4
  module BBC
5
5
  module Cosmos
6
6
  module Tools
7
7
  module Cloudformation
8
8
  class Generator
9
-
10
- RESERVED_MAIN_STACK_PARAMS = ['Environment', 'PublicSubnet1Id', 'PublicSubnet2Id', 'PublicSubnet3Id', 'ImageId', 'VpcId', 'PrivateSubnet1Id', 'PrivateSubnet2Id', 'PrivateSubnet3Id', 'BastionAccessSecurityGroup', 'DomainNameBase', 'KeyName', 'S3BucketArn']
9
+ RESERVED_MAIN_STACK_PARAMS = %w(Environment PublicSubnet1Id PublicSubnet2Id PublicSubnet3Id ImageId VpcId PrivateSubnet1Id PrivateSubnet2Id PrivateSubnet3Id BastionAccessSecurityGroup DomainNameBase KeyName S3BucketArn)
11
10
 
12
11
  def self.component_exists?(component, config)
13
- config.resources['cloudformation']['components'][component]
12
+ config.resources["cloudformation"]["components"][component]
14
13
  end
15
14
 
16
15
  def self.stack_exists?(component, config, stack)
17
- config.resources['cloudformation']['components'][component][stack]
16
+ config.resources["cloudformation"]["components"][component][stack]
18
17
  end
19
18
 
20
19
  def self.create(component, config, options, to_json = false, &block)
@@ -22,28 +21,28 @@ module BBC
22
21
  template.declare(&block)
23
22
 
24
23
  if template.checkRefs || CfnDsl::Errors.errors?
25
- self.raise_error template
24
+ raise_error template
26
25
  else
27
26
  hash = JSON.parse(template.to_json)
28
- hash['Parameters'] ||= {}
27
+ hash["Parameters"] ||= {}
29
28
 
30
29
  if component_exists?(component, config) && stack_exists?(component, config, options[:stack])
31
30
  component_parameters(config.resources, options, component).each do |key, value|
32
- hash['Parameters'][key]['Default'] = value if hash['Parameters'][key]
31
+ hash["Parameters"][key]["Default"] = value if hash["Parameters"][key]
33
32
  end
34
33
  end
35
34
 
36
- hash.delete_if { |k, v| k == 'Parameters' && v == {} }
35
+ hash.delete_if { |k, v| k == "Parameters" && v == {} }
37
36
 
38
37
  to_json ? JSON.pretty_generate(hash) : hash
39
38
  end
40
39
  end
41
40
 
42
41
  def self.parameters(data, main_stack = true)
43
- if data['Parameters']
44
- data['Parameters'].reduce({}) do |object, (key, value)|
42
+ if data["Parameters"]
43
+ data["Parameters"].reduce({}) do |object, (key, value)|
45
44
  object.tap do |o|
46
- o[key] = value['Default'] unless reserved_main_stack_param_for(key, main_stack) && value['Default'].nil?
45
+ o[key] = value["Default"] unless reserved_main_stack_param_for(key, main_stack) && value["Default"].nil?
47
46
  end
48
47
  end
49
48
  else
@@ -53,27 +52,27 @@ module BBC
53
52
 
54
53
  def self.raise_error(template)
55
54
  if CfnDsl::Errors.errors?
56
- raise("Errors in template: #{CfnDsl::Errors.errors.join(' # ')}")
57
- elsif (invalid_references = template.checkRefs())
58
- raise("Invalid Refs: #{invalid_references.join(' # ')}")
55
+ fail("Errors in template: #{CfnDsl::Errors.errors.join(' # ')}")
56
+ elsif (invalid_references = template.checkRefs)
57
+ fail("Invalid Refs: #{invalid_references.join(' # ')}")
59
58
  end
60
59
  end
61
60
 
62
61
  private
63
62
 
64
63
  def self.component_stack_details(resources, options, component)
65
- raise("cloudformation: -> components: node is missing from the environment resource file") unless resources.has_key?("cloudformation") && resources['cloudformation'].has_key?("components")
64
+ fail("cloudformation: -> components: node is missing from the environment resource file") unless resources.key?("cloudformation") && resources["cloudformation"].key?("components")
66
65
 
67
- components = resources['cloudformation']['components']
68
- raise("'#{component}' node missing from cloudformation: -> components: nodes") unless components.has_key?(component)
66
+ components = resources["cloudformation"]["components"]
67
+ fail("'#{component}' node missing from cloudformation: -> components: nodes") unless components.key?(component)
69
68
  component = components[component]
70
69
 
71
- raise("'#{options[:stack]}' stack node missing from cloudformation: -> components: -> #{component}:") unless component.has_key?(options[:stack])
70
+ fail("'#{options[:stack]}' stack node missing from cloudformation: -> components: -> #{component}:") unless component.key?(options[:stack])
72
71
  component[options[:stack]]
73
72
  end
74
73
 
75
74
  def self.variants(component_meta, variant = nil)
76
- component_meta.has_key?('variants') ? component_meta['variants'][variant] : component_meta.fetch('parameters', {})
75
+ component_meta.key?("variants") ? component_meta["variants"][variant] : component_meta.fetch("parameters", {})
77
76
  end
78
77
 
79
78
  def self.component_parameters(resources, options, component)
@@ -84,10 +83,8 @@ module BBC
84
83
  def self.reserved_main_stack_param_for(key, main_stack)
85
84
  main_stack && RESERVED_MAIN_STACK_PARAMS.include?(key)
86
85
  end
87
-
88
86
  end
89
87
  end
90
88
  end
91
89
  end
92
90
  end
93
-
@@ -1,6 +1,6 @@
1
1
  require "thor"
2
2
  require "pathname"
3
- require 'json'
3
+ require "json"
4
4
  require "bbc/cosmos/tools/config"
5
5
 
6
6
  module BBC
@@ -14,10 +14,10 @@ module BBC
14
14
 
15
15
  package_name "BBC Cosmos config tool"
16
16
 
17
- class_option :key_path, :type => :string, :default => ENV['BBC_COSMOS_TOOLS_CERT'], :desc => "The path to your dev cert, by default the value of the env variable $BBC_COSMOS_TOOLS_CERT is used"
18
- class_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
17
+ class_option :key_path, :type => :string, :default => ENV["BBC_COSMOS_TOOLS_CERT"], :desc => "The path to your dev cert, by default the value of the env variable $BBC_COSMOS_TOOLS_CERT is used"
18
+ class_option :env, :type => :string, :default => "int", :desc => "The environment to target"
19
19
  class_option :base, :type => :string, :desc => "The base path for the configs repo"
20
- class_option :project, :type => :string, :default => ENV['BBC_COSMOS_TOOLS_PROJECT'], :desc => "The name of the project, by default the value of the env variable $BBC_COSMOS_TOOLS_PROJECT is used"
20
+ class_option :project, :type => :string, :default => ENV["BBC_COSMOS_TOOLS_PROJECT"], :desc => "The name of the project, by default the value of the env variable $BBC_COSMOS_TOOLS_PROJECT is used"
21
21
 
22
22
  def initialize(args = [], local_options = {}, config = {})
23
23
  super(args, local_options, config)
@@ -33,7 +33,7 @@ module BBC
33
33
  def banner(component = nil)
34
34
  output = component.nil? ? "\n" : "\n#{get_key_value('Component', component)}\n"
35
35
  options.reduce(output) do |string, (key, value)|
36
- string += "#{get_key_value(key.capitalize.gsub(/_/, " "), value)}\n"
36
+ string += "#{get_key_value(key.capitalize.gsub(/_/, ' '), value)}\n"
37
37
  end
38
38
  end
39
39
 
@@ -42,7 +42,7 @@ module BBC
42
42
  end
43
43
 
44
44
  def api_error(response, die = true)
45
- error_message = JSON.parse(response.body)['message']
45
+ error_message = JSON.parse(response.body)["message"]
46
46
  error(error_message, die)
47
47
  end
48
48
 
@@ -66,18 +66,16 @@ module BBC
66
66
  private
67
67
 
68
68
  def setup_config
69
- config_base = !options[:base].nil? && File.directory?(options[:base]) ? Pathname.new(options[:base]) : Pathname.pwd.join('.')
70
- raise("Config directory: #{config_base} doesn't exist") unless File.directory?(config_base)
69
+ config_base = !options[:base].nil? && File.directory?(options[:base]) ? Pathname.new(options[:base]) : Pathname.pwd.join(".")
70
+ fail("Config directory: #{config_base} doesn't exist") unless File.directory?(config_base)
71
71
  @config = BBC::Cosmos::Tools::Config.new(
72
72
  config_base,
73
73
  options[:project],
74
74
  options[:env]
75
75
  )
76
76
  end
77
-
78
77
  end
79
78
  end
80
79
  end
81
80
  end
82
81
  end
83
-
@@ -1,13 +1,13 @@
1
1
  require "bbc/cosmos/tools/api"
2
2
  require "aws-sdk"
3
3
  require "json"
4
+ require "highline/import"
4
5
 
5
6
  module BBC
6
7
  module Cosmos
7
8
  module Tools
8
9
  module Commands
9
10
  class Component < Base
10
-
11
11
  desc "instances <COMPONENT>", "Show instances of the given component"
12
12
  method_option :show_tag, :type => :string, :desc => "Show tag"
13
13
  def instances(component)
@@ -18,46 +18,106 @@ module BBC
18
18
  tag = options[:show_tag]
19
19
 
20
20
  rows = instance_ids(component).map do |id|
21
- [id].tap { |row|
22
- row << ec2.instances[id].tags[tag] if tag
23
- }
21
+ [id].tap do |row|
22
+ row << ec2.instances[id].tags[tag] if tag
23
+ end
24
24
  end
25
25
 
26
- headers = ['Instance ID']
26
+ headers = ["Instance ID"]
27
27
  headers << tag if tag
28
28
 
29
29
  print_table build_table(headers, rows)
30
30
  end
31
31
 
32
+ desc "SSH <COMPONENT>", "Gain SSH"
33
+ def ssh(component)
34
+ meta = make_request("env/#{options[:env]}/component/#{component}/instances")
35
+
36
+ meta.count == 1 ? single_instance(component, meta)
37
+ : multiple_instance(component, meta)
38
+ end
39
+
32
40
  private
41
+
42
+ def api
43
+ @api ||= BBC::Cosmos::Tools::API.new(
44
+ "https://api.live.bbc.co.uk", options[:key_path]
45
+ )
46
+ end
47
+
48
+ def single_instance(component, meta)
49
+ session(component, meta[0]["id"], meta[0]["private_ip_address"])
50
+ end
51
+
52
+ def multiple_instance(component, meta)
53
+ instance_id = select_instance(meta)
54
+ session(component, instance_id, extract_ipaddress(meta, instance_id))
55
+ end
56
+
57
+ def session(component, instance_id, ip_address)
58
+ post_data(component, instance_id)
59
+ output_message(component, ip_address, instance_id)
60
+ end
61
+
62
+ def select_instance(meta)
63
+ choose do |menu|
64
+ say "Multiple instances detected, please select one", :green
65
+
66
+ meta.map do |i|
67
+ menu.choice(i["id"].to_sym)
68
+ end
69
+ end.to_s
70
+ end
71
+
72
+ def extract_ipaddress(meta, instance)
73
+ meta.detect { |hash| hash.value?(instance) }.fetch("private_ip_address")
74
+ end
75
+
76
+ def output_message(component, ip, id)
77
+ say "SSH setup for #{component} on #{options[:env]}, your IP for #{id} is #{ip}", :green
78
+ end
79
+
80
+ def post_data(component, id)
81
+ post_data = { "instance_id" => id }
82
+
83
+ endpoint = "/cosmos/env/#{options[:env]}/component/#{component}/logins/create"
84
+ api.post(endpoint, post_data.to_json).tap do |response|
85
+ fail Exception, response.body if response.status != 201
86
+ end
87
+ end
88
+
89
+ def make_request(path)
90
+ JSON.parse(api.get("/cosmos/#{path}").tap do |request|
91
+ fail Exception, request.body if request.status != 200
92
+ end.body)
93
+ end
94
+
33
95
  def instance_ids(component)
34
96
  response = BBC::Cosmos::Tools::API.new(
35
- config.app['api'],
97
+ config.app["api"],
36
98
  options[:key_path]
37
- )
38
- .get "/cosmos/env/#{options[:env]}/component/#{component}/instances"
99
+ ).get "/cosmos/env/#{options[:env]}/component/#{component}/instances"
39
100
 
40
- raise Exception, response.body if response.status != 200
101
+ fail Exception, response.body if response.status != 200
41
102
 
42
- JSON.parse(response.body).map { |instance|
103
+ JSON.parse(response.body).map do |instance|
43
104
  instance.fetch "id"
44
- }
105
+ end
45
106
  end
46
107
 
47
108
  def aws_credentials
48
- response = BBC::Cosmos::Tools::API.new(
49
- "https://wormhole.cloud.bbc.co.uk",
50
- options[:key_path]
51
- )
52
- .get "/login/credentials"
109
+ response = BBC::Cosmos::Tools::API.new(
110
+ "https://wormhole.cloud.bbc.co.uk",
111
+ options[:key_path]
112
+ ).get "/login/credentials"
53
113
 
54
- raise Exception, response.body if response.status != 200
114
+ fail Exception, response.body if response.status != 200
55
115
 
56
116
  credentials = JSON.parse response.body
57
117
 
58
118
  { :access_key_id => credentials["accessKeyId"],
59
- :secret_access_key => credentials['secretAccessKey'],
60
- :session_token => credentials['sessionToken'],
119
+ :secret_access_key => credentials["secretAccessKey"],
120
+ :session_token => credentials["sessionToken"],
61
121
  :region => "eu-west-1" }
62
122
  end
63
123
  end
@@ -1,8 +1,8 @@
1
- require 'json'
2
- require 'pathname'
3
- require 'bbc/cosmos/tools/commands/base'
4
- require 'bbc/cosmos/tools/cosmos_configuration'
5
- require 'bbc/cosmos/tools/api'
1
+ require "json"
2
+ require "pathname"
3
+ require "bbc/cosmos/tools/commands/base"
4
+ require "bbc/cosmos/tools/cosmos_configuration"
5
+ require "bbc/cosmos/tools/api"
6
6
 
7
7
  module BBC
8
8
  module Cosmos
@@ -22,12 +22,11 @@ module BBC
22
22
  method_option :cosmos_format, :type => :boolean, :desc => "Toogles if the output should be in the cosmos format"
23
23
  def generate(component)
24
24
  if options[:write]
25
- File.open("#{component}.json", 'w') do |f|
25
+ File.open("#{component}.json", "w") do |f|
26
26
  f.write(JSON.pretty_generate(cosmos_config.component(component)))
27
27
  end
28
28
  end
29
29
 
30
-
31
30
  if options[:output] == "machine"
32
31
  say JSON.pretty_generate(cosmos_config.cosmos_component(component)), :white
33
32
 
@@ -46,7 +45,6 @@ module BBC
46
45
  rescue Exception => e
47
46
  error e.message
48
47
  end
49
-
50
48
  end
51
49
 
52
50
  desc "config list", "Lists all components for a given project"
@@ -71,48 +69,41 @@ module BBC
71
69
  rescue Exception => e
72
70
  error e.message
73
71
  end
74
-
75
72
  end
76
73
 
77
74
  desc "config push [COMPONENT_ID = nil]", "Push config for a component to the cosmos API"
78
75
  method_option :force, :type => :boolean, :desc => "This skips asking of you want to push the changes"
79
76
  def push(component_id = nil)
80
- begin
77
+ components = component_id.nil? ? cosmos_config.component_keys : [component_id]
78
+ say "#{banner}\n"
81
79
 
82
- components = component_id.nil? ? cosmos_config.component_keys : [component_id]
83
- say "#{banner}\n"
80
+ reply = yes?("Are you sure you want to push changes for #{components.length} components(s) to #{options[:env]}?", :yellow) unless options[:force]
81
+ if reply || reply.nil?
84
82
 
85
- reply = yes?("Are you sure you want to push changes for #{components.length} components(s) to #{options[:env]}?", :yellow) unless options[:force]
86
- if reply || reply.nil?
83
+ components.each do |id|
84
+ say "\n#{get_key_value('Component', id)}\n"
87
85
 
88
- components.each do |id|
86
+ component_config = JSON.generate(cosmos_config.cosmos_component id)
89
87
 
90
- say "\n#{get_key_value('Component', id)}\n"
88
+ @api = BBC::Cosmos::Tools::API.new(config.app["api"], options[:key_path])
89
+ response = @api.put sprintf(config.app["config_endpoint"], options[:env], id), component_config
91
90
 
92
- component_config = JSON.generate(cosmos_config.cosmos_component id)
93
-
94
- @api = BBC::Cosmos::Tools::API.new(config.app['api'], options[:key_path])
95
- response = @api.put sprintf(config.app['config_endpoint'], options[:env], id), component_config
96
-
97
- if response.success?
98
- say 'Successfully saved', :green
99
- else
100
- api_error response
101
- end
91
+ if response.success?
92
+ say "Successfully saved", :green
93
+ else
94
+ api_error response
102
95
  end
103
-
104
- else
105
- error 'Action cancelled'
106
96
  end
107
97
 
108
- rescue Exception => e
109
- error e.message
98
+ else
99
+ error "Action cancelled"
110
100
  end
111
- end
112
101
 
102
+ rescue Exception => e
103
+ error e.message
104
+ end
113
105
  end
114
106
  end
115
107
  end
116
108
  end
117
109
  end
118
-