bbc-cosmos-tools 0.0.6 → 0.0.7

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: eaf0d70ac4348428e9a88d7c793be076cfe110e8
4
- data.tar.gz: 605e4e41c4a1b12f40befac74d7709e5fea70751
3
+ metadata.gz: ac699293cfb105c83aa421fb01338d7c29c3ceda
4
+ data.tar.gz: c5a2f30919bc440a2039052991fa14fde1e7cce5
5
5
  SHA512:
6
- metadata.gz: e99485c53617e7c5c0f3c45f2d00649371409bca2949ac5df0de91e46ffac12a120e749ed8f2147f2e259dabe75284b0f8e2e2eb0e24283f6e9c9d9197baca38
7
- data.tar.gz: f101152cf7ec14b515594796f513d4f2fdc664945b14f0eb452ac2df24d375c889e69f5ea78fcd09bee8e4415c4546ac896db4d32fe6ed7bf995920dd585022d
6
+ metadata.gz: 41c9cdc07e529c981d43145402395fcc9a10014b708465a798f4326c746b0007ce1e6dd50c474e571575ec74468dd1b844a695769ba986abca45374163197450
7
+ data.tar.gz: 0145fff6eff60745ba45fd9a93d565960ac28af7b3ac6727afa28e378621eba7669db2bdae403a5936aa5db36dbaefeea7012c8ca297e122279a1315bac0d04a
@@ -1,6 +1,8 @@
1
1
  require "thor"
2
- require_relative 'commands/config'
3
- require_relative 'commands/cloud_formation'
2
+ require 'bbc/cosmos/tools/commands/config'
3
+ require 'bbc/cosmos/tools/commands/cloud_formation'
4
+ require 'bbc/cosmos/tools/commands/release'
5
+ require 'bbc/cosmos/tools/commands/stack'
4
6
 
5
7
  module BBC
6
8
  module Cosmos
@@ -14,6 +16,13 @@ module BBC
14
16
  desc "cf", "Cloudformation sub commands"
15
17
  subcommand "cf", Commands::CloudFormation
16
18
 
19
+ desc "release", "Cosmos release sub commands"
20
+ subcommand "release", Commands::Release
21
+
22
+ desc "stack", "Cosmos stack sub commands"
23
+ subcommand "stack", Commands::Stack
24
+
25
+
17
26
  end
18
27
  end
19
28
  end
@@ -9,10 +9,10 @@ module BBC
9
9
 
10
10
  def self.create(component, config, options, to_json = false, &block)
11
11
 
12
- template = CfnDsl::CloudFormationTemplate.new
12
+ template = BBC::CfnDsl::CloudFormationTemplate.new
13
13
  template.declare(&block)
14
14
 
15
- if template.checkRefs() || CfnDsl::Errors.errors?
15
+ if template.checkRefs() || BBC::CfnDsl::Errors.errors?
16
16
  self.raise_error template
17
17
  else
18
18
  hash = JSON.parse(template.to_json)
@@ -14,11 +14,50 @@ module BBC
14
14
 
15
15
  package_name "BBC Cosmos config tool"
16
16
 
17
+ class_option :key_path, :type => :string, :default => ENV['DEV_CERT_PATH'], :desc => "The path to your dev cert, by default it looks in the env variable '$DEV_CERT_PATH'"
18
+ class_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
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"
21
+
17
22
  def initialize(args = [], local_options = {}, config = {})
18
23
  super(args, local_options, config)
19
24
  setup_config
20
25
  end
21
26
 
27
+ protected
28
+
29
+ def get_key_value(key, value)
30
+ "#{set_color("#{key}:", :cyan)} #{set_color(value, :white)}"
31
+ end
32
+
33
+ def banner(component = nil)
34
+ output = component.nil? ? "\n" : "\n#{get_key_value('Component', component)}\n"
35
+ options.reduce(output) do |string, (key, value)|
36
+ string += "#{get_key_value(key.capitalize.gsub(/_/, " "), value)}\n"
37
+ end
38
+ end
39
+
40
+ def components(options, component = nil)
41
+ components = component.nil? ? config.components_for(options[:tags]).keys : [component]
42
+ end
43
+
44
+ def api_error(message, response)
45
+ error(message, false)
46
+ error(JSON.pretty_generate(response.body))
47
+ end
48
+
49
+ def build_table(headers, data)
50
+ [].tap do |table|
51
+ table << headers.reduce([]) do |array, header|
52
+ array << set_color(header, :cyan)
53
+ end
54
+
55
+ data.each do |items|
56
+ table << items.map { |item| set_color(item, :white) }
57
+ end
58
+ end
59
+ end
60
+
22
61
  private
23
62
 
24
63
  def setup_config
@@ -31,9 +70,9 @@ module BBC
31
70
  )
32
71
  end
33
72
 
34
- def error(message)
73
+ def error(message, die = true)
35
74
  say message, :red
36
- exit 1
75
+ exit 1 if die
37
76
  end
38
77
 
39
78
  end
@@ -8,32 +8,31 @@ module BBC
8
8
  module Commands
9
9
  class CloudFormation < Base
10
10
 
11
+ class_option :stack, :type => :string, :default => 'main', :desc => "The name of the stack to use"
12
+ class_option :variant, :type => :string, :default => 'default', :desc => "The variant to use, i.e default|scheduled|"
13
+ class_option :group, :type => :string, :default => nil, :desc => "The group name to override the component id"
14
+
15
+ def initialize(args = [], local_options = {}, config = {})
16
+ super(args, local_options, config)
17
+ end
18
+
11
19
  desc "cf generate [COMPONENT]", "Generates a cloudformation template based on a type of component, the identifier and the environment"
12
- method_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
13
- method_option :stack, :type => :string, :default => 'main', :desc => "The name of the stack to use"
14
- method_option :variant, :type => :string, :default => 'default', :desc => "The variant to use, i.e default|scheduled|"
15
- method_option :group, :type => :string, :default => nil, :desc => "The group name to override the component id"
16
- method_option :project, :type => :string, :required => true, :desc => "The name of the project"
17
20
  def generate(component)
18
21
 
19
- say cloudformation(component, options), :green
22
+ say "#{banner(component)}\n"
23
+ say cloudformation(component, options), :white
20
24
 
21
25
  end
22
26
 
23
27
  desc "cf create [COMPONENT, STACK_NAME = 'main', MAIN_STACK = false]", "Generates and create the cloudformation template for the specific component"
24
- method_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
25
- method_option :stack, :type => :string, :default => 'main', :desc => "The name of the stack to use"
26
- method_option :variant, :type => :string, :default => 'default', :desc => "The variant to use, i.e default|scheduled|"
27
- method_option :group, :type => :string, :default => nil, :desc => "The group name to override the component id"
28
- method_option :project, :type => :string, :required => true, :desc => "The name of the project"
28
+ def create(component, main_stack = false)
29
29
 
30
- method_option :key_path, :type => :string, :default => ENV['DEV_CERT_PATH'], :desc => "The path to your dev cert, by default it looks in the env variable '$DEV_CERT_PATH'"
31
- def create(component, stack_name = 'main', main_stack = false)
30
+ say "#{banner(component)}\n"
32
31
 
33
- data = cloudformation(component_identifier, options, false)
32
+ data = cloudformation(component, options, false)
34
33
 
35
34
  post_data = {
36
- 'name_suffix' => stack_name,
35
+ 'name_suffix' => options[:stack],
37
36
  'service_stack' => main_stack,
38
37
  'template' => data,
39
38
  'parameters' => Tools::Cloudformation::Generator.parameters(data),
@@ -45,20 +44,16 @@ module BBC
45
44
  if response.success?
46
45
  say "Stack creation for component '#{component}' successfully started", :green
47
46
  else
48
- say "There was an error creating your stack: #{response.body}", :red
47
+ api_error("There was an error creating your stack", response.body)
49
48
  end
50
49
 
51
50
  end
52
51
 
53
52
  desc "cf update [COMPONENT]", "Generates and updates the cloudformation template for the specific component"
54
- method_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
55
- method_option :stack, :type => :string, :default => 'main', :desc => "The name of the stack to use"
56
- method_option :variant, :type => :string, :default => 'default', :desc => "The variant to use, i.e default|scheduled|"
57
- method_option :group, :type => :string, :default => nil, :desc => "The group name to override the component id"
58
- method_option :project, :type => :string, :required => true, :desc => "The name of the project"
59
- method_option :key_path, :type => :string, :default => ENV['DEV_CERT_PATH'], :desc => "The path to your dev cert, by default it looks in the env variable '$DEV_CERT_PATH'"
60
53
  def update(component)
61
54
 
55
+ say "#{banner(component)}\n"
56
+
62
57
  stack_name = "#{options[:env]}-#{component}-#{options[:stack]}"
63
58
  data = cloudformation(component, options, false)
64
59
 
@@ -73,7 +68,7 @@ module BBC
73
68
  if response.success?
74
69
  say "Stack update for component '#{component}' successfully started", :green
75
70
  else
76
- say "There was an error updating your stack: #{response.body}", :red
71
+ api_error('There was an error updating your stack', response.body)
77
72
  end
78
73
 
79
74
  end
@@ -18,24 +18,15 @@ module BBC
18
18
 
19
19
  desc "config generate [COMPONENT]", "Generates config for the given component optionally in the cosmos format"
20
20
  method_option :cosmos_format, :type => :boolean, :desc => "Toogles if the output should be in the cosmos format"
21
- method_option :base, :type => :string, :desc => "The base path for the configs repo"
22
- method_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
23
- method_option :project, :type => :string, :required => true, :desc => "The name of the project"
24
21
  def generate(component)
25
22
  begin
26
23
 
27
- message = <<-output.gsub /^\s+/, ""
28
- Component: #{component}
29
- Project: #{options[:project]}
30
- Environement: #{options[:env]}
31
- output
32
-
33
- say "\n#{message}\n", :green
24
+ say "#{banner(component)}\n"
34
25
 
35
26
  if options[:cosmos_format]
36
- say JSON.pretty_generate(cosmos_config.cosmos_component(component))
27
+ say JSON.pretty_generate(cosmos_config.cosmos_component(component)), :white
37
28
  else
38
- say JSON.pretty_generate(cosmos_config.component(component))
29
+ say JSON.pretty_generate(cosmos_config.component(component)), :white
39
30
  end
40
31
  rescue Exception => e
41
32
  error e.message
@@ -44,22 +35,14 @@ module BBC
44
35
  end
45
36
 
46
37
  desc "config list", "Lists all components for a given project"
47
- method_option :base, :type => :string, :desc => "The base path for the configs repo"
48
- method_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
49
- method_option :project, :type => :string, :required => true, :desc => "The name of the project"
50
38
  def list
51
39
  begin
52
40
 
53
- message = <<-output.gsub /^\s+/, ""
54
- Project: #{options[:project]}
55
- Environement: #{options[:env]}
56
- Components:
57
- output
58
-
59
- say "\n#{message}\n", :green
41
+ say banner
42
+ say "\n#{get_key_value('Components', '')}\n"
60
43
 
61
44
  cosmos_config.component_keys.each do |component_id|
62
- say "- #{component_id}", :blue
45
+ say "* #{component_id}", :white
63
46
  end
64
47
  rescue Exception => e
65
48
  error e.message
@@ -69,37 +52,36 @@ module BBC
69
52
 
70
53
  desc "config push [COMPONENT_ID = nil]", "Push config for a component to the cosmos API"
71
54
  method_option :force, :type => :boolean, :desc => "This skips asking of you want to push the changes"
72
- method_option :base, :type => :string, :desc => "The base path for the configs repo"
73
- method_option :key_path, :type => :string, :default => ENV['DEV_CERT_PATH'], :desc => "The path to your dev cert, by default it looks in the env variable '$DEV_CERT_PATH'"
74
- method_option :env, :type => :string, :default => 'int', :desc => "The environment to target"
75
- method_option :project, :type => :string, :required => true, :desc => "The name of the project"
76
55
  def push(component_id = nil)
77
56
  begin
78
57
 
79
58
  components = component_id.nil? ? cosmos_config.component_keys : [component_id]
59
+ say "#{banner}\n"
80
60
 
81
- reply = yes?("Are you sure you want to push changes for #{components.length} components(s) to #{options[:env]}?", :blue) unless options[:force]
61
+ reply = yes?("Are you sure you want to push changes for #{components.length} components(s) to #{options[:env]}?", :yellow) unless options[:force]
82
62
  if reply || reply.nil?
83
63
 
84
64
  components.each do |id|
65
+
66
+ say "\n#{get_key_value('Component', id)}\n"
67
+
85
68
  component_config = JSON.generate(cosmos_config.cosmos_component id)
86
69
 
87
70
  @api = BBC::Cosmos::Tools::API.new(config.app['api'], options[:key_path])
88
71
  response = @api.put sprintf(config.app['config_endpoint'], options[:env], id), component_config
89
72
 
90
73
  if response.success?
91
- say "Configuration for component '#{id}' successfully saved", :green
74
+ say 'Successfully saved', :green
92
75
  else
93
- say "There was an error saving your config: #{response.body}", :red
76
+ api_error('Error pusing config', response.body)
94
77
  end
95
78
  end
96
79
 
97
80
  else
98
- say "Action cancelled", :red
81
+ error 'Action cancelled'
99
82
  end
100
83
 
101
84
  rescue Exception => e
102
- puts e.backtrace
103
85
  error e.message
104
86
  end
105
87
  end
@@ -0,0 +1,158 @@
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
+
7
+ module BBC
8
+ module Cosmos
9
+ module Tools
10
+ module Commands
11
+ class Release < Base
12
+ attr_accessor :api
13
+
14
+ class_option :limit, :type => :numeric, :default => 1, :desc => "Limit how many entries to show"
15
+
16
+ def initialize(args = [], local_options = {}, thor_config = {})
17
+ super(args, local_options, thor_config)
18
+ @api = BBC::Cosmos::Tools::API.new(config.app['api'], options[:key_path])
19
+ end
20
+
21
+ method_option :tags, :type => :array, :default => nil
22
+ desc "release list [COMPONENT = nil]", "Lists releases for a component"
23
+ def list(component = nil)
24
+ begin
25
+
26
+ say banner
27
+
28
+ components(options, component).each do |id|
29
+
30
+ response = api.get sprintf(config.app['release'], id)
31
+ say get_key_value("\nComponent", id)
32
+
33
+ if response.success?
34
+ data = []
35
+ JSON.parse(response.body)['releases'].slice!(0, options[:limit]).map do |release|
36
+ data << [release['version'], release['created_at']]
37
+ end
38
+
39
+ print_table(
40
+ build_table(['Version', 'Created at'], data)
41
+ )
42
+ say "\n"
43
+ else
44
+ api_error('There was a problem retrieving the releases for this component', response)
45
+ end
46
+ end
47
+
48
+ rescue Exception => e
49
+ error e.message
50
+ end
51
+ end
52
+
53
+ desc "release deploy [COMPONENT = nil, RELEASE_ID = nil]", "Deploys a release to the specific enironment"
54
+ method_option :tags, :type => :array, :default => nil
55
+ def deploy(component = nil, release_id = nil)
56
+ begin
57
+
58
+ say banner
59
+
60
+ components(options, component).each do |id|
61
+
62
+ component_release = release_id
63
+ if release_id.nil?
64
+ response = api.get sprintf(config.app['release'], id)
65
+ component_release = JSON.parse(response.body)['releases'].first['version']
66
+ end
67
+
68
+ post_data = JSON.generate({ 'release_version' => component_release })
69
+ response = api.post sprintf(config.app['deploy'], options[:env], id), post_data
70
+
71
+ if response.success?
72
+
73
+ say get_key_value("\nComponent", id)
74
+ say get_key_value("Version", component_release)
75
+
76
+ data = JSON.parse(response.body)
77
+ say "Deployment started successfully: #{config.app['api']}#{data['url']}\n", :green
78
+
79
+ else
80
+ api_error("There was a problem deploying this component", response)
81
+ end
82
+
83
+ end
84
+
85
+ rescue Exception => e
86
+ error e.message
87
+ end
88
+ end
89
+
90
+ desc "release deployed [COMPONENT = nil]", "Lists releases deloyed for a component in the specified environment"
91
+ def deployed(component = nil)
92
+ begin
93
+
94
+ say banner
95
+
96
+ components(options, component).each do |id|
97
+
98
+ response = api.get sprintf(config.app['deployments'], id, options[:env])
99
+ say get_key_value("\nComponent", id)
100
+
101
+ if response.success?
102
+ data = JSON.parse(response.body)
103
+
104
+ if data.empty?
105
+ say "There are currently no deployments for this component", :yellow
106
+ else
107
+
108
+ deployments = []
109
+
110
+ data.slice!(0, options[:limit].to_i).map do |release|
111
+ deployments << [
112
+ release['release']['version'],
113
+ release['started_at'],
114
+ release['id'],
115
+ release['created_by']['email_address'],
116
+ deploy_status(release['status'])
117
+ ]
118
+ end
119
+
120
+ print_table(
121
+ build_table(['Release', 'Deployed at', 'Deployment id', 'Deployed by', 'Status'], deployments)
122
+ )
123
+
124
+ say "\n"
125
+ end
126
+ else
127
+ api_error('There was a problem retrieving the releases for this component', response)
128
+ end
129
+ end
130
+
131
+ rescue Exception => e
132
+ error e.message
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def deploy_status(status)
139
+ case status
140
+ when 'pending_bake'
141
+ set_color(status, :yellow)
142
+ when 'pending_stack_update_resolution'
143
+ set_color(status, :yellow)
144
+ when 'done'
145
+ set_color(status, :green)
146
+ when 'failed'
147
+ set_color(status, :red)
148
+ else
149
+ set_color(status, :white)
150
+ end
151
+ end
152
+
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+
@@ -0,0 +1,69 @@
1
+ require "bbc/cosmos/tools/config"
2
+ require 'bbc/cosmos/tools/commands/base'
3
+ require 'bbc/cosmos/tools/cloudformation/generator'
4
+
5
+ module BBC
6
+ module Cosmos
7
+ module Tools
8
+ module Commands
9
+ class Stack < Base
10
+ attr_accessor :api
11
+
12
+ def initialize(args = [], local_options = {}, thor_config = {})
13
+ super(args, local_options, thor_config)
14
+ @api = BBC::Cosmos::Tools::API.new(config.app['api'], options[:key_path])
15
+ end
16
+
17
+ desc "stack list [COMPONENT]", "Lists stacks for a component"
18
+ def list(component)
19
+
20
+ say "#{banner}\n"
21
+ response = api.get sprintf(config.app['stacks'], options[:env], component)
22
+
23
+ data = JSON.parse(response.body).map do |stack|
24
+ [
25
+ stack['name'],
26
+ stack['main_stack'],
27
+ stack['updated_at'],
28
+ stack['status']
29
+ ]
30
+ end
31
+
32
+ print_table(
33
+ build_table(
34
+ ['Name', 'Main stack', 'Updated at', 'Status'],
35
+ data
36
+ )
37
+ )
38
+
39
+ end
40
+
41
+ desc "stack events [COMPONENT]", "Shows the stack events for a component"
42
+ method_option :stack, :type => :string, :default => 'main', :desc => "The name of the stack to use"
43
+ method_option :limit, :type => :numeric, :default => 5, :desc => "Limit how many records to show"
44
+ def events(component)
45
+
46
+ say "#{banner}\n"
47
+
48
+ stack_name = "#{options[:env]}-#{component}-#{options[:stack]}"
49
+ response = api.get sprintf(config.app['stack_events'], options[:env], component, stack_name)
50
+ events = JSON.parse(response.body)
51
+
52
+ data = events.slice(0,(options[:limit])).map do |event|
53
+ event.split(" ")
54
+ end
55
+
56
+ print_table(
57
+ build_table(
58
+ ['Event type', 'CF type', 'Name', 'Status'],
59
+ data
60
+ )
61
+ )
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
@@ -18,7 +18,7 @@ module BBC
18
18
  if File.exist? path
19
19
  YAML::load(File.open(path).read)
20
20
  else
21
- raise("#{project} doesn't exist")
21
+ raise("Invalid project, please set the $BBC_COSMOS_TOOLS_PROJECT environment variable or use the --project parameter")
22
22
  end
23
23
  else
24
24
  raise("#{env} is an invalid environment")
@@ -38,6 +38,12 @@ module BBC
38
38
  end
39
39
  end
40
40
 
41
+ def components_for(tags)
42
+ resources['cloudformation']['components'].select do |component_id, data|
43
+ data['tags'].any? { |tag| tags.nil? ? true : tags.include?(tag) }
44
+ end
45
+ end
46
+
41
47
  def app
42
48
  config_path = base_path.join('configs', 'app.yaml')
43
49
  raise("Invalid application config path: #{config_path}") unless File.exists? config_path
@@ -1,7 +1,7 @@
1
1
  module BBC
2
2
  module Cosmos
3
3
  module Tools
4
- VERSION = "0.0.6"
4
+ VERSION = "0.0.7"
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bbc-cosmos-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Jack
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-02 00:00:00.000000000 Z
11
+ date: 2014-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -102,6 +102,8 @@ files:
102
102
  - lib/bbc/cosmos/tools/commands/base.rb
103
103
  - lib/bbc/cosmos/tools/commands/cloud_formation.rb
104
104
  - lib/bbc/cosmos/tools/commands/config.rb
105
+ - lib/bbc/cosmos/tools/commands/release.rb
106
+ - lib/bbc/cosmos/tools/commands/stack.rb
105
107
  - lib/bbc/cosmos/tools/config.rb
106
108
  - lib/bbc/cosmos/tools/cosmos_configuration.rb
107
109
  - lib/bbc/cosmos/tools/version.rb