ministryofjustice/Claim-for-Crown-Court-Defence

View on GitHub
.k8s/bin/secret

Summary

Maintainability
Test Coverage
#!/usr/bin/env ruby
#
# secret
# Usage:
#   kubernetes_deploy/bin/secret -e [dev, staging, etc] options.
#
# Notes:
# - Contexts are those available in your kubernetes config.
#   if the current-context is the one you wish to query then you
#   do not needed to specify it.
# - For a list of secrets to query `kubectl get secrets`
#
# Examples:
# most basic example if current-context is the one you want
# `kubernetes_deploy/bin/secret -e dev`
#
# output base64 decoded cccd-secrets oject to STDOUT
# `kubernetes_deploy/bin/secret -c live -e dev -d`
#
# write base64 decoded cccd-secrets oject to a .gitignored temp file
# `kubernetes_deploy/bin/secret -c live -e dev -dw`
#
# query the s3 bucket secrets
# `kubernetes_deploy/bin/secret -c live -e dev -d -s cccd-s3-bucket`
#
require 'optparse'
require 'ostruct'
require 'yaml'
require 'base64'

# rubocop:disable Metrics/MethodLength, Metrics/BlockLength, Metrics/AbcSize
class SecretOptParser
  ENVIRONMENTS = %w[dev staging api-sandbox production].freeze

  def self.parse(args)
    options = OpenStruct.new
    options.context = `kubectl config current-context`.chomp
    options.decode = false
    options.write = false
    options.secret = 'cccd-secrets'

    secret_opt_parser = OptionParser.new do |opts|
      opts.banner = "Usage: #{__FILE__} [options]"
      opts.separator ''
      opts.separator 'Specific options:'

      opts.on('-e',
              '--env ENVIRONMENT',
              'Required: the Environment for executing your script',
              " (#{ENVIRONMENTS.join(', ')})") do |env|
        options.environment = env
      end

      opts.on('-c',
              '--context [CONTEXT]',
              'Optional: Set context, defaults to k8s current-context') do |context|
        options.context = context
      end

      opts.on('-s',
              '--secret [SECRETS_OBJECT]',
              'Optional: the secret object from which to retrieve key-value pairs (default cccd-secrets)') do |secret|
        options.secret = secret
      end

      opts.on('-d',
              '--[no-]decode',
              'Optional: Decode base64, defaults to false') do |decode|
        options.decode = decode
      end

      opts.on(
        '-w', '--[no-]write',
        <<~OPT
          Optional: write output to file as yaml,
          Always writes to temp_<options.environment>_secrets.yaml
        OPT
      ) do |write|
        options.write = write
      end

      opts.on_tail('-h', '--help', 'Show this message') do
        puts opts
        exit
      end
    end

    secret_opt_parser.parse!(args)
    options
  end
end
# rubocop:enable Metrics/MethodLength, Metrics/BlockLength, Metrics/AbcSize

def options
  @options ||= SecretOptParser.parse(ARGV)
end

def grep_excludes
  @grep_excludes ||= 'annotations|last-applied-configuration|creationTimestamp|resourceVersion|selfLink|uid'
end

def write_file(secrets)
  file = File.join('.k8s', options.context, options.environment, "temp_#{options.environment}_secrets.yaml")
  File.write(file, secrets)
  puts "secrets written to #{file}"
end

def check_requirements
  return unless options.environment.nil? || options.context.nil?
  puts 'context and environment required!'
  exit
end

def secrets
  return @secrets unless @secrets.nil?
  puts "Running command: #{secret_cmd}"
  @secrets = YAML.safe_load(`#{secret_cmd}`.chomp)
  @secrets['data'].transform_values! { |v| Base64.decode64(v) unless v.nil? } if options.decode
  @secrets
end

def secret_cmd
  @secret_cmd ||= "kubectl --context #{options.context} -n cccd-#{options.environment} get secret #{options.secret} -o yaml | grep -vE '#{grep_excludes}'"
end

# entrypoint
check_requirements
options.write ? write_file(secrets.to_yaml) : puts(secrets.to_yaml)