google-ssl-cert 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +6 -0
  6. data/Guardfile +19 -0
  7. data/LICENSE.txt +1 -0
  8. data/README.md +153 -0
  9. data/Rakefile +14 -0
  10. data/docs/google-secrets-cheatsheet.md +6 -0
  11. data/exe/google-ssl-cert +14 -0
  12. data/google-ssl-cert.gemspec +43 -0
  13. data/lib/google-ssl-cert.rb +1 -0
  14. data/lib/google_ssl_cert/autoloader.rb +22 -0
  15. data/lib/google_ssl_cert/base.rb +14 -0
  16. data/lib/google_ssl_cert/cert.rb +85 -0
  17. data/lib/google_ssl_cert/cli/base.rb +12 -0
  18. data/lib/google_ssl_cert/cli/create.rb +67 -0
  19. data/lib/google_ssl_cert/cli/help/completion.md +20 -0
  20. data/lib/google_ssl_cert/cli/help/completion_script.md +3 -0
  21. data/lib/google_ssl_cert/cli/help/create.md +31 -0
  22. data/lib/google_ssl_cert/cli/help/prune.md +30 -0
  23. data/lib/google_ssl_cert/cli/help/secret/get.md +4 -0
  24. data/lib/google_ssl_cert/cli/help/secret/save.md +4 -0
  25. data/lib/google_ssl_cert/cli/help.rb +11 -0
  26. data/lib/google_ssl_cert/cli/prune.rb +88 -0
  27. data/lib/google_ssl_cert/cli/secret.rb +15 -0
  28. data/lib/google_ssl_cert/cli.rb +62 -0
  29. data/lib/google_ssl_cert/command.rb +89 -0
  30. data/lib/google_ssl_cert/completer/script.rb +8 -0
  31. data/lib/google_ssl_cert/completer/script.sh +10 -0
  32. data/lib/google_ssl_cert/completer.rb +159 -0
  33. data/lib/google_ssl_cert/global.rb +12 -0
  34. data/lib/google_ssl_cert/google_services.rb +30 -0
  35. data/lib/google_ssl_cert/helpers/global.rb +7 -0
  36. data/lib/google_ssl_cert/helpers/project_number.rb +15 -0
  37. data/lib/google_ssl_cert/logger.rb +28 -0
  38. data/lib/google_ssl_cert/logging.rb +9 -0
  39. data/lib/google_ssl_cert/name.rb +19 -0
  40. data/lib/google_ssl_cert/secret.rb +86 -0
  41. data/lib/google_ssl_cert/version.rb +3 -0
  42. data/lib/google_ssl_cert.rb +13 -0
  43. data/spec/cli_spec.rb +26 -0
  44. data/spec/spec_helper.rb +29 -0
  45. metadata +272 -0
@@ -0,0 +1,88 @@
1
+ class GoogleSslCert::CLI
2
+ class Prune < Base
3
+ include GoogleSslCert::Helpers::ProjectNumber
4
+
5
+ def run
6
+ keep = @options[:keep] || 1
7
+ right = -1 - keep
8
+ certs = ssl_certs[0..right] || [] # delete all except the last cert
9
+
10
+ if certs.empty?
11
+ logger.info "No timestamped certs to prune with cert name: #{cert_base_name}"
12
+ return
13
+ end
14
+
15
+ preview_delete(certs) unless @options[:yes]
16
+ sure?
17
+ perform_delete(certs)
18
+ end
19
+
20
+ def preview_delete(certs)
21
+ logger.info "Will delete the following #{type} certs:"
22
+ certs.each do |cert|
23
+ logger.info " #{cert.name}"
24
+ end
25
+ end
26
+
27
+ def perform_delete(certs)
28
+ certs.each do |cert|
29
+ delete(cert)
30
+ end
31
+ end
32
+
33
+ def delete(cert)
34
+ options = base_options.merge(ssl_certificate: cert.name)
35
+ ssl_service.delete(options)
36
+ logger.info "Deleted #{type} cert: #{cert.name}"
37
+ end
38
+
39
+ def type
40
+ global? ? "global" : "region"
41
+ end
42
+
43
+ # sadly the filter option doesnt support globs or regexp so will have to filter with ruby
44
+ def ssl_certs
45
+ resp = ssl_service.list(base_options)
46
+ resp.select do |ssl|
47
+ match?(ssl.name)
48
+ end.sort_by(&:name)
49
+ end
50
+
51
+ def match?(name)
52
+ !!(name =~ Regexp.new("^#{cert_base_name}-\\d{14}$"))
53
+ end
54
+
55
+ def cert_base_name
56
+ @cert_base_name = GoogleSslCert::Name.new(@options).base_name
57
+ end
58
+
59
+ def ssl_service
60
+ if global?
61
+ ssl_certificates
62
+ else
63
+ region_ssl_certificates
64
+ end
65
+ end
66
+
67
+ def base_options
68
+ options = { project: ENV['GOOGLE_PROJECT'] }
69
+ options[:region] = ENV['GOOGLE_REGION'] unless global?
70
+ options
71
+ end
72
+
73
+ private
74
+ def sure?(message="Are you sure?")
75
+ if @options[:yes]
76
+ sure = 'y'
77
+ else
78
+ print "#{message} (y/N) "
79
+ sure = $stdin.gets
80
+ end
81
+
82
+ unless sure =~ /^y/
83
+ puts "Whew! Exiting."
84
+ exit 0
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,15 @@
1
+ class GoogleSslCert::CLI
2
+ class Secret < GoogleSslCert::Command
3
+ desc "save", "Save secret value"
4
+ long_desc Help.text("secret/save")
5
+ def save(name, value)
6
+ GoogleSslCert::Secret.new(options).save(name, value)
7
+ end
8
+
9
+ desc "get", "Get secret value"
10
+ def get(name)
11
+ value = GoogleSslCert::Secret.new(options).get(name)
12
+ puts "Secret name: #{name} value #{value}"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,62 @@
1
+ module GoogleSslCert
2
+ class CLI < Command
3
+ class_option :verbose, type: :boolean
4
+ class_option :noop, type: :boolean
5
+
6
+ secret_name_option = Proc.new do
7
+ option :secret_name, desc: "Secret name, conventionally matches the cert name"
8
+ end
9
+ global_option = Proc.new do
10
+ option :global, type: :boolean, default: true, desc: "Flag to create global vs region cert"
11
+ end
12
+ cert_name_option = Proc.new do
13
+ option :cert_name, desc: "Google SSL Cert name"
14
+ end
15
+
16
+ desc "create", "Create Google SSL Certificate and save to Secrets Manager"
17
+ long_desc Help.text(:create)
18
+ option :private_key, desc: "private key path"
19
+ option :certificate, desc: "certificate path"
20
+ option :save_secret, type: :boolean, default: true, desc: "whether or not to save to Google Secrets Manager"
21
+ option :extra_certs, desc: "Additional certs to be added to the secret value"
22
+ option :timestamp, type: :boolean, default: true, desc: "Auto-append timestamp to cert name. Appending a timestamp allows auto-pruning also"
23
+ option :prune, type: :boolean, default: true, desc: "Auto-prune old certs based on timestamp"
24
+ cert_name_option.call
25
+ secret_name_option.call
26
+ global_option.call
27
+ def create
28
+ Create.new(options).run
29
+ end
30
+
31
+ desc "prune", "prune Google SSL Certificate and save to Secrets Manager"
32
+ long_desc Help.text(:prune)
33
+ cert_name_option.call
34
+ global_option.call
35
+ option :yes, aliases: %w[y], type: :boolean, desc: "Skip 'are you sure' prompt"
36
+ option :keep, type: :numeric, default: 1, desc: "Number of certs to keep"
37
+ def prune
38
+ Prune.new(options).run
39
+ end
40
+
41
+ desc "secret SUBCOMMAND", "secret subcommands"
42
+ long_desc Help.text(:secret)
43
+ subcommand "secret", Secret
44
+
45
+ desc "completion *PARAMS", "Prints words for auto-completion."
46
+ long_desc Help.text(:completion)
47
+ def completion(*params)
48
+ Completer.new(CLI, *params).run
49
+ end
50
+
51
+ desc "completion_script", "Generates a script that can be eval to setup auto-completion."
52
+ long_desc Help.text(:completion_script)
53
+ def completion_script
54
+ Completer::Script.generate
55
+ end
56
+
57
+ desc "version", "prints version"
58
+ def version
59
+ puts VERSION
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,89 @@
1
+ require "thor"
2
+
3
+ # Override thor's long_desc identation behavior
4
+ # https://github.com/erikhuda/thor/issues/398
5
+ class Thor
6
+ module Shell
7
+ class Basic
8
+ def print_wrapped(message, options = {})
9
+ message = "\n#{message}" unless message[0] == "\n"
10
+ stdout.puts message
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module GoogleSslCert
17
+ class Command < Thor
18
+ class << self
19
+ def dispatch(m, args, options, config)
20
+ # Allow calling for help via:
21
+ # google-ssl-cert command help
22
+ # google-ssl-cert command -h
23
+ # google-ssl-cert command --help
24
+ # google-ssl-cert command -D
25
+ #
26
+ # as well thor's normal way:
27
+ #
28
+ # google-ssl-cert help command
29
+ help_flags = Thor::HELP_MAPPINGS + ["help"]
30
+ if args.length > 1 && !(args & help_flags).empty?
31
+ args -= help_flags
32
+ args.insert(-2, "help")
33
+ end
34
+
35
+ # google-ssl-cert version
36
+ # google-ssl-cert --version
37
+ # google-ssl-cert -v
38
+ version_flags = ["--version", "-v"]
39
+ if args.length == 1 && !(args & version_flags).empty?
40
+ args = ["version"]
41
+ end
42
+
43
+ super
44
+ end
45
+
46
+ # Override command_help to include the description at the top of the
47
+ # long_description.
48
+ def command_help(shell, command_name)
49
+ meth = normalize_command_name(command_name)
50
+ command = all_commands[meth]
51
+ alter_command_description(command)
52
+ super
53
+ end
54
+
55
+ def alter_command_description(command)
56
+ return unless command
57
+
58
+ # Add description to beginning of long_description
59
+ long_desc = if command.long_description
60
+ "#{command.description}\n\n#{command.long_description}"
61
+ else
62
+ command.description
63
+ end
64
+
65
+ # add reference url to end of the long_description
66
+ unless website.empty?
67
+ full_command = [command.ancestor_name, command.name].compact.join('-')
68
+ url = "#{website}/reference/google-ssl-cert-#{full_command}"
69
+ long_desc += "\n\nHelp also available at: #{url}"
70
+ end
71
+
72
+ command.long_description = long_desc
73
+ end
74
+ private :alter_command_description
75
+
76
+ # meant to be overriden
77
+ def website
78
+ ""
79
+ end
80
+
81
+ # https://github.com/erikhuda/thor/issues/244
82
+ # Deprecation warning: Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `Lono::CLI`
83
+ # You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.
84
+ def exit_on_failure?
85
+ true
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,8 @@
1
+ class GoogleSslCert::Completer
2
+ class Script
3
+ def self.generate
4
+ bash_script = File.expand_path("script.sh", File.dirname(__FILE__))
5
+ puts "source #{bash_script}"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ _google-ssl-cert() {
2
+ COMPREPLY=()
3
+ local word="${COMP_WORDS[COMP_CWORD]}"
4
+ local words=("${COMP_WORDS[@]}")
5
+ unset words[0]
6
+ local completion=$(google-ssl-cert completion ${words[@]})
7
+ COMPREPLY=( $(compgen -W "$completion" -- "$word") )
8
+ }
9
+
10
+ complete -F _google-ssl-cert google-ssl-cert
@@ -0,0 +1,159 @@
1
+ =begin
2
+ Code Explanation:
3
+
4
+ There are 3 types of things to auto-complete:
5
+
6
+ 1. command: the command itself
7
+ 2. parameters: command parameters.
8
+ 3. options: command options
9
+
10
+ Here's an example:
11
+
12
+ mycli hello name --from me
13
+
14
+ * command: hello
15
+ * parameters: name
16
+ * option: --from
17
+
18
+ When command parameters are done processing, the remaining completion words will be options. We can tell that the command params are completed based on the method arity.
19
+
20
+ ## Arity
21
+
22
+ For example, say you had a method for a CLI command with the following form:
23
+
24
+ ufo scale service count --cluster development
25
+
26
+ It's equivalent ruby method:
27
+
28
+ scale(service, count) = has an arity of 2
29
+
30
+ So typing:
31
+
32
+ ufo scale service count [TAB] # there are 3 parameters including the "scale" command according to Thor's CLI processing.
33
+
34
+ So the completion should only show options, something like this:
35
+
36
+ --noop --verbose --cluster
37
+
38
+ ## Splat Arguments
39
+
40
+ When the ruby method has a splat argument, it's arity is negative. Here are some example methods and their arities.
41
+
42
+ ship(service) = 1
43
+ scale(service, count) = 2
44
+ ships(*services) = -1
45
+ foo(example, *rest) = -2
46
+
47
+ Fortunately, negative and positive arity values are processed the same way. So we take simply take the absolute value of the arity and process it the same.
48
+
49
+ Here are some test cases, hit TAB after typing the command:
50
+
51
+ google-ssl-cert completion
52
+ google-ssl-cert completion hello
53
+ google-ssl-cert completion hello name
54
+ google-ssl-cert completion hello name --
55
+ google-ssl-cert completion hello name --noop
56
+
57
+ google-ssl-cert completion
58
+ google-ssl-cert completion sub:goodbye
59
+ google-ssl-cert completion sub:goodbye name
60
+
61
+ ## Subcommands and Thor::Group Registered Commands
62
+
63
+ Sometimes the commands are not simple thor commands but are subcommands or Thor::Group commands. A good specific example is the ufo tool.
64
+
65
+ * regular command: ufo ship
66
+ * subcommand: ufo docker
67
+ * Thor::Group command: ufo init
68
+
69
+ Auto-completion accounts for each of these type of commands.
70
+ =end
71
+ module GoogleSslCert
72
+ class Completer
73
+ def initialize(command_class, *params)
74
+ @params = params
75
+ @current_command = @params[0]
76
+ @command_class = command_class # CLI initiall
77
+ end
78
+
79
+ def run
80
+ if subcommand?(@current_command)
81
+ subcommand_class = @command_class.subcommand_classes[@current_command]
82
+ @params.shift # destructive
83
+ Completer.new(subcommand_class, *@params).run # recursively use subcommand
84
+ return
85
+ end
86
+
87
+ # full command has been found!
88
+ unless found?(@current_command)
89
+ puts all_commands
90
+ return
91
+ end
92
+
93
+ # will only get to here if command aws found (above)
94
+ arity = @command_class.instance_method(@current_command).arity.abs
95
+ if @params.size > arity or thor_group_command?
96
+ puts options_completion
97
+ else
98
+ puts params_completion
99
+ end
100
+ end
101
+
102
+ def subcommand?(command)
103
+ @command_class.subcommands.include?(command)
104
+ end
105
+
106
+ # hacky way to detect that command is a registered Thor::Group command
107
+ def thor_group_command?
108
+ command_params(raw=true) == [[:rest, :args]]
109
+ end
110
+
111
+ def found?(command)
112
+ public_methods = @command_class.public_instance_methods(false)
113
+ command && public_methods.include?(command.to_sym)
114
+ end
115
+
116
+ # all top-level commands
117
+ def all_commands
118
+ commands = @command_class.all_commands.reject do |k,v|
119
+ v.is_a?(Thor::HiddenCommand)
120
+ end
121
+ commands.keys
122
+ end
123
+
124
+ def command_params(raw=false)
125
+ params = @command_class.instance_method(@current_command).parameters
126
+ # Example:
127
+ # >> Sub.instance_method(:goodbye).parameters
128
+ # => [[:req, :name]]
129
+ # >>
130
+ raw ? params : params.map!(&:last)
131
+ end
132
+
133
+ def params_completion
134
+ offset = @params.size - 1
135
+ offset_params = command_params[offset..-1]
136
+ command_params[offset..-1].first
137
+ end
138
+
139
+ def options_completion
140
+ used = ARGV.select { |a| a.include?('--') } # so we can remove used options
141
+
142
+ method_options = @command_class.all_commands[@current_command].options.keys
143
+ class_options = @command_class.class_options.keys
144
+
145
+ all_options = method_options + class_options + ['help']
146
+
147
+ all_options.map! { |o| "--#{o.to_s.gsub('_','-')}" }
148
+ filtered_options = all_options - used
149
+ filtered_options.uniq
150
+ end
151
+
152
+ # Useful for debugging. Using puts messes up completion.
153
+ def log(msg)
154
+ File.open("/tmp/complete.log", "a") do |file|
155
+ file.puts(msg)
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,12 @@
1
+ module GoogleSslCert
2
+ class Global
3
+ def initialize(options={})
4
+ @options = options
5
+ end
6
+
7
+ def global?
8
+ default_global = !%w[0 false].include?(ENV['GSC_GLOBAL']) # nil will default to true
9
+ @options[:global].nil? ? default_global : @options[:global]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ require "google-cloud-resource_manager"
2
+ require "google-cloud-secret_manager"
3
+ require "google/cloud/compute/v1/region_ssl_certificates"
4
+ require "google/cloud/compute/v1/ssl_certificates"
5
+
6
+ module GoogleSslCert
7
+ module GoogleServices
8
+ extend Memoist
9
+
10
+ def region_ssl_certificates
11
+ Google::Cloud::Compute::V1::RegionSslCertificates::Rest::Client.new
12
+ end
13
+ memoize :region_ssl_certificates
14
+
15
+ def secret_manager_service
16
+ Google::Cloud::SecretManager.secret_manager_service
17
+ end
18
+ memoize :secret_manager_service
19
+
20
+ def ssl_certificates
21
+ Google::Cloud::Compute::V1::SslCertificates::Rest::Client.new
22
+ end
23
+ memoize :ssl_certificates
24
+
25
+ def resource_manager
26
+ Google::Cloud.new.resource_manager
27
+ end
28
+ memoize :resource_manager
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module GoogleSslCert::Helpers
2
+ module Global
3
+ def global?
4
+ GoogleSslCert::Global.new(@options).global?
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module GoogleSslCert::Helpers
2
+ module ProjectNumber
3
+ private
4
+ def parent
5
+ "projects/#{project_number}"
6
+ end
7
+
8
+ @@project_number = nil
9
+ def project_number
10
+ return @@project_number if @@project_number
11
+ project = resource_manager.project(ENV['GOOGLE_PROJECT'])
12
+ @@project_number = project.project_number
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ require 'logger'
2
+
3
+ module GoogleSslCert
4
+ class Logger < ::Logger
5
+ def initialize(*args)
6
+ super
7
+ self.formatter = Formatter.new
8
+ self.level = log_level
9
+ end
10
+
11
+ def log_level
12
+ if ENV['DEBUG']
13
+ :debug
14
+ else
15
+ ENV['GSC_LOG_LEVEL'] || :info # note: only respected when config.logger not set in config/app.rb
16
+ end
17
+ end
18
+
19
+ def format_message(severity, datetime, progname, msg)
20
+ line = if @logdev.dev == $stdout || @logdev.dev == $stderr
21
+ msg # super simple format if stdout
22
+ else
23
+ super # use the configured formatter
24
+ end
25
+ line =~ /\n$/ ? line : "#{line}\n"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ module GoogleSslCert
2
+ module Logging
3
+ extend Memoist
4
+
5
+ def logger
6
+ $logger ||= Logger.new($stderr)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module GoogleSslCert
2
+ class Name
3
+ attr_reader :base_name
4
+ def initialize(options={})
5
+ @options = options
6
+ @base_name = @options[:cert_name] || default_cert_name
7
+ end
8
+
9
+ def generate
10
+ ts = Time.now.strftime("%Y%m%d%H%M%S") unless @options[:timestamp] == false # nil defaults to true
11
+ [@base_name, ts].compact.join('-')
12
+ end
13
+
14
+ def default_cert_name
15
+ type = @options[:global] ? "global" : ENV['GOOGLE_REGION']
16
+ ["google-ssl-cert", type].join('-')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,86 @@
1
+ module GoogleSslCert
2
+ class Secret < Base
3
+ extend Memoist
4
+
5
+ # CLI commands:
6
+ # gcloud secrets create testsecret
7
+ # gcloud secrets versions add testsecret --data-file="/tmp/testsecret.txt"
8
+ #
9
+ # Secret create API docs
10
+ # https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets#secretmanager-create-secret-ruby
11
+ # https://github.com/googleapis/google-cloud-ruby/blob/af60d07b8f134ebc35bee795d127be614abea353/google-cloud-secret_manager-v1/lib/google/cloud/secret_manager/v1/secret_manager_service/client.rb#L307
12
+ # https://cloud.google.com/secret-manager/docs/reference/rest/v1/projects.secrets/create
13
+ # Secret Versions add API docs
14
+ # https://github.com/googleapis/google-cloud-ruby/blob/af60d07b8f134ebc35bee795d127be614abea353/google-cloud-secret_manager-v1/lib/google/cloud/secret_manager/v1/secret_manager_service/client.rb#L379
15
+ # https://cloud.google.com/secret-manager/docs/reference/rest/v1/projects.secrets/addVersion
16
+ # https://cloud.google.com/secret-manager/docs/reference/rest/v1/SecretPayload
17
+ def save(name, value)
18
+ validate!
19
+ create_secret(name, value)
20
+ url_path = "#{parent}/secrets/#{name}"
21
+ secret_manager_service.add_secret_version(parent: url_path, payload: {data: value})
22
+ logger.info "Secret saved: name: #{name} value: #{value}"
23
+ rescue Google::Cloud::AlreadyExistsError => e
24
+ logger.error("#{e.class}: #{e.message}")
25
+ end
26
+
27
+ def create_secret(name, value)
28
+ secret = get_secret(name)
29
+ return if secret
30
+ secret_manager_service.create_secret(
31
+ parent: parent,
32
+ secret_id: name,
33
+ secret: {
34
+ replication: {
35
+ automatic: {}
36
+ }
37
+ }
38
+ )
39
+ end
40
+
41
+ def get_secret(name)
42
+ url_path = "#{parent}/secrets/#{name}"
43
+ secret_manager_service.get_secret(name: url_path)
44
+ rescue Google::Cloud::NotFoundError
45
+ nil
46
+ rescue Google::Cloud::InvalidArgumentError => e
47
+ logger.fatal("ERROR: #{e.class}: #{e.message}\n")
48
+ logger.fatal("Expected format: [[a-zA-Z_0-9]+]")
49
+ exit 1
50
+ end
51
+
52
+ # CLI commands:
53
+ # gcloud secrets list
54
+ # gcloud secrets versions access latest --secret testsecret
55
+ #
56
+ # Secret access version API docs
57
+ # https://cloud.google.com/secret-manager/docs/reference/rest/v1/projects.secrets.versions/access
58
+ # https://cloud.google.com/secret-manager/docs/reference/rest/v1/SecretPayload
59
+ def get(name)
60
+ version = @options[:version] || "latest"
61
+ url_path = "#{parent}/secrets/#{name}/versions/#{version}"
62
+ version = secret_manager_service.access_secret_version(name: url_path)
63
+ version.payload.data
64
+ rescue Google::Cloud::NotFoundError => e
65
+ logger.error "WARN: secret #{name.color(:yellow)} not found"
66
+ logger.error e.message
67
+ "NOT FOUND #{name}" # simple string so Kubernetes YAML is valid
68
+ end
69
+
70
+ def validate!
71
+ errors = []
72
+ secret_name = @options[:secret_name]
73
+ if @options[:save_secret] && !secret_name
74
+ errors << "ERROR: --secret-name must be provided or --no-save-secret option must be used"
75
+ end
76
+ # extra validation early to prevent google ssl cert from being created but the secret not being stored
77
+ if secret_name && secret_name !~ /^[a-zA-Z_\-0-9]+$/
78
+ errors << "ERROR: --secret-name invalid format. Expected format: [a-zA-Z_0-9]+" # Expected format taken from `gcloud secrets create`
79
+ end
80
+ unless errors.empty?
81
+ logger.error errors.join("\n")
82
+ exit 1
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,3 @@
1
+ module GoogleSslCert
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ $stdout.sync = true unless ENV["GOOGLE_SSL_CERT_STDOUT_SYNC"] == "0"
2
+
3
+ $:.unshift(File.expand_path("../", __FILE__))
4
+
5
+ require "google_ssl_cert/autoloader"
6
+ GoogleSslCert::Autoloader.setup
7
+
8
+ require "memoist"
9
+ require "rainbow/ext/string"
10
+
11
+ module GoogleSslCert
12
+ class Error < StandardError; end
13
+ end