bbc-cosmos-tools 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/bbc-cosmos-tools.gemspec +5 -4
- data/bin/bbc-cosmos-tools +3 -3
- data/lib/bbc/cosmos/tools/api.rb +6 -8
- data/lib/bbc/cosmos/tools/app.rb +4 -4
- data/lib/bbc/cosmos/tools/cloudformation/generator.rb +20 -23
- data/lib/bbc/cosmos/tools/commands/base.rb +8 -10
- data/lib/bbc/cosmos/tools/commands/component.rb +79 -19
- data/lib/bbc/cosmos/tools/commands/config.rb +24 -33
- data/lib/bbc/cosmos/tools/commands/release.rb +100 -112
- data/lib/bbc/cosmos/tools/commands/stack.rb +60 -72
- data/lib/bbc/cosmos/tools/config.rb +19 -22
- data/lib/bbc/cosmos/tools/cosmos_configuration.rb +4 -6
- data/lib/bbc/cosmos/tools/types/base_type.rb +6 -7
- data/lib/bbc/cosmos/tools/types/cfndsl.rb +2 -3
- data/lib/bbc/cosmos/tools/version.rb +1 -1
- metadata +19 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 682aa789050e6b06223d07a9da105df6abdc966d
|
4
|
+
data.tar.gz: 2256eaa0c3a4c060fb26fdaa8fa637796602723c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8cb7f2f87c0d45e710caa0fd6f85b887410927bd37570894cfbee4eb88d7bd8df4c2976a14bd2a5e05bb505b52e95a950ec5ea7192b55e9fdf81c22643264188
|
7
|
+
data.tar.gz: 9b4b400d1c6cd2da4bb81c22518dfb5ee8e80217a9b7f3879bbec47cd50edbce62bf9a0478c87131e4f60b37b9318b68150f1170d101565815b45a78ffa021cc
|
data/Gemfile
CHANGED
data/bbc-cosmos-tools.gemspec
CHANGED
@@ -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 =
|
12
|
-
spec.summary =
|
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
|
3
|
+
require "pathname"
|
4
4
|
|
5
5
|
root = Pathname.new(__FILE__).dirname.parent
|
6
|
-
lib_path = (root +
|
6
|
+
lib_path = (root + "lib").realdirpath
|
7
7
|
|
8
8
|
$LOAD_PATH.unshift(lib_path)
|
9
9
|
|
10
|
-
require
|
10
|
+
require "bbc/cosmos/tools/app"
|
11
11
|
|
12
12
|
include BBC::Cosmos::Tools
|
13
13
|
|
data/lib/bbc/cosmos/tools/api.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
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 =>
|
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 =
|
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 =
|
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
|
data/lib/bbc/cosmos/tools/app.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require "thor"
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
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
|
2
|
-
require
|
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[
|
12
|
+
config.resources["cloudformation"]["components"][component]
|
14
13
|
end
|
15
14
|
|
16
15
|
def self.stack_exists?(component, config, stack)
|
17
|
-
config.resources[
|
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
|
-
|
24
|
+
raise_error template
|
26
25
|
else
|
27
26
|
hash = JSON.parse(template.to_json)
|
28
|
-
hash[
|
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[
|
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 ==
|
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[
|
44
|
-
data[
|
42
|
+
if data["Parameters"]
|
43
|
+
data["Parameters"].reduce({}) do |object, (key, value)|
|
45
44
|
object.tap do |o|
|
46
|
-
o[key] = value[
|
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
|
-
|
57
|
-
elsif (invalid_references = template.checkRefs
|
58
|
-
|
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
|
-
|
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[
|
68
|
-
|
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
|
-
|
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.
|
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
|
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[
|
18
|
-
class_option :env, :type => :string, :default =>
|
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[
|
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(/_/,
|
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)[
|
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
|
-
|
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
|
22
|
-
|
23
|
-
|
21
|
+
[id].tap do |row|
|
22
|
+
row << ec2.instances[id].tags[tag] if tag
|
23
|
+
end
|
24
24
|
end
|
25
25
|
|
26
|
-
headers = [
|
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[
|
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
|
-
|
101
|
+
fail Exception, response.body if response.status != 200
|
41
102
|
|
42
|
-
JSON.parse(response.body).map
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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[
|
60
|
-
:session_token => credentials[
|
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
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
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",
|
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
|
-
|
77
|
+
components = component_id.nil? ? cosmos_config.component_keys : [component_id]
|
78
|
+
say "#{banner}\n"
|
81
79
|
|
82
|
-
|
83
|
-
|
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
|
-
|
86
|
-
|
83
|
+
components.each do |id|
|
84
|
+
say "\n#{get_key_value('Component', id)}\n"
|
87
85
|
|
88
|
-
|
86
|
+
component_config = JSON.generate(cosmos_config.cosmos_component id)
|
89
87
|
|
90
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
response
|
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
|
-
|
109
|
-
error
|
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
|
-
|