bbc-cosmos-tools 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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