class CLI

rubocop:disable Metrics/ClassLength

Public Instance Methods

run() click to toggle source

rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metric/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength

# File lib/cli.rb, line 15
def run
  program :name, 'cry - cryptopus cli'
  program :version, '1.0.0'
  program :description, 'CLI tool to manage Openshift Secrets via Cryptopus'
  program :help, 'Source Code', 'https://www.github.com/puzzle/ccli'
  program :help, 'Usage', 'cry [flags]'

  command :login do |c|
    c.syntax = 'cry login <credentials>'
    c.description = 'Logs in to the ccli'

    c.action do |args|
      token, url = extract_login_args(args)
      execute_action do
        session_adapter.update_session({ encoded_token: token, url: url })

        # Test authentification by calling teams endpoint
        Team.all

        log_success 'Successfully logged in'
      end
    end
  end

  command :logout do |c|
    c.syntax = 'cry logout'
    c.description = 'Logs out of the ccli'

    c.action do
      execute_action do
        session_adapter.clear_session
        log_success 'Successfully logged out'
      end
    end
  end

  command :account do |c|
    c.syntax = 'cry account <id> [options]'
    c.description = 'Fetches an account by the given id'
    c.option '--username', String, 'Only show the username of the user'
    c.option '--password', String, 'Only show the password of the user'

    c.action do |args, options|
      exit_with_error(:usage_error, 'id missing') if args.empty?
      execute_action do
        logger.info 'Fetching account...'
        account = Account.find(args.first)
        out = account.username if options.username
        out = account.password if options.password
        puts out || account.to_yaml
      end
    end
  end

  command :folder do |c|
    c.syntax = 'cry folder <id>'
    c.description = 'Selects the Cryptopus folder by id'

    c.action do |args|
      id = args.first
      exit_with_error(:usage_error, 'id missing') unless id
      exit_with_error(:usage_error, 'id invalid') unless id.match?(/(^\d{1,10}$)/)

      execute_action do
        session_adapter.update_session({ folder: id })

        log_success "Selected Folder with id: #{id}"
      end
    end
  end

  command :'ose-secret-pull' do |c|
    c.syntax = 'cry ose-secret-pull <secret-name>'
    c.summary = 'Pulls secret from Openshift to Cryptopus'
    c.description = "Pulls the Secret from Openshift and pushes them to Cryptopus.\n" \
                    'If a Cryptopus Account in the selected folder using the name ' \
                    "of the given secret is already present, it will be updated accordingly.\n" \
                    'If no name is given, it will pull all secrets inside the selected project.'

    c.action do |args|
      if args.length > 1
        exit_with_error(:usage_error,
                        'Only a single or no arguments are allowed')
      end

      execute_action({ secret_name: args.first }) do
        if args.empty?
          logger.info 'Fetching secrets...'
          OSESecret.all.each do |secret|
            logger.info "Saving secret #{secret.name}..."
            cryptopus_adapter.save_secret(secret)
            log_success "Saved secret #{secret.name} in Cryptopus"
          end
        elsif args.length == 1
          logger.info "Saving secret #{args.first}..."
          cryptopus_adapter.save_secret(OSESecret.find_by_name(args.first))
          log_success "Saved secret #{args.first} in Cryptopus"
        end
      end
    end
  end

  command :'ose-secret-push' do |c|
    c.syntax = 'cry ose-secret-push <secret-name>'
    c.summary = 'Pushes secret from Cryptopus to Openshift'
    c.description = 'Pushes the Secret to Openshift by retrieving it from Cryptopus first. ' \
                    'If a Secret in the selected Openshift project using the name ' \
                    'of the given accountname is already present, it will be updated accordingly.'

    c.action do |args|
      secret_name = args.first
      exit_with_error(:usage_error, 'Only one secret can be pushed') if args.length > 1
      execute_action({ secret_name: secret_name }) do
        secret_accounts = if secret_name.nil?
                            logger.info 'Fetching all accounts in folder...'
                            session_adapter.selected_folder.accounts
                          else
                            logger.info "Fetching account #{secret_name}..."
                            [cryptopus_adapter.find_account_by_name(secret_name)]
                          end
        secret_accounts.each do |account|
          logger.info "Fetching secret #{account.accountname}..."
          secret_account = Account.find(account.id)
          logger.info "Inserting secret #{account.accountname}..."
          ose_adapter.insert_secret(secret_account.to_osesecret)
          log_success "Secret #{secret_account.accountname} was successfully applied"
        end
      end
    end
  end

  command :'k8s-secret-pull' do |c|
    c.syntax = 'cry k8s-secret-pull <secret-name>'
    c.summary = 'Pulls secret from Kubectl to Cryptopus'
    c.description = "Pulls the Secret from Kubectl and pushes them to Cryptopus.\n" \
                    'If a Cryptopus Account in the selected folder using the name ' \
                    "of the given secret is already present, it will be updated accordingly.\n" \
                    'If no name is given, it will pull all secrets inside the selected project.'

    c.action do |args|
      if args.length > 1
        TTY::Exit.exit_with(:usage_error,
                            'Only a single or no arguments are allowed')
      end

      execute_action({ secret_name: args.first }) do
        if args.empty?
          logger.info 'Fetching secrets...'
          K8SSecret.all.each do |secret|
            logger.info "Saving secret #{secret.name}..."
            cryptopus_adapter.save_secret(secret)
            log_success "Saved secret #{secret.name} in Cryptopus"
          end
        elsif args.length == 1
          logger.info "Saving secret #{args.first}..."
          cryptopus_adapter.save_secret(K8SSecret.find_by_name(args.first))
          log_success "Saved secret #{args.first} in Cryptopus"
        end
      end
    end
  end

  command :'k8s-secret-push' do |c|
    c.syntax = 'cry k8s-secret-push <secret-name>'
    c.summary = 'Pushes secret from Cryptopus to Kubectl'
    c.description = 'Pushes the Secret to Kubectl by retrieving it from Cryptopus first. ' \
                    'If a Secret in the selected Kubectl project using the name ' \
                    'of the given accountname is already present, it will be updated accordingly.'

    c.action do |args|
      secret_name = args.first
      exit_with_error(:usage_error, 'Only one secret can be pushed') if args.length > 1
      execute_action({ secret_name: secret_name }) do
        secret_accounts = if secret_name.nil?
                            logger.info 'Fetching all accounts in folder...'
                            session_adapter.selected_folder.accounts
                          else
                            logger.info "Fetching account #{secret_name}..."
                            [cryptopus_adapter.find_account_by_name(secret_name)]
                          end
        secret_accounts.each do |account|
          logger.info "Fetching secret #{account.accountname}..."
          secret_account = Account.find(account.id)
          logger.info "Inserting secret #{account.accountname}..."
          k8s_adapter.insert_secret(secret_account.to_osesecret)
          log_success "Secret #{secret_account.accountname} was successfully applied"
        end
      end
    end
  end

  command :teams do |c|
    c.syntax = 'cry teams'
    c.description = 'Lists all available teams'

    c.action do
      execute_action do
        logger.info 'Fetching teams...'
        teams = Team.all
        output = teams.map(&:render_list).join("\n")
        puts output
      end
    end
  end

  command :use do |c|
    c.syntax = 'cry use <team/folder>'
    c.description = 'Select the current folder'

    c.action do |args|
      team_name, folder_name = extract_use_args(args)
      execute_action({ team_name: team_name, folder_name: folder_name }) do
        logger.info "Looking for team #{team_name}..."
        selected_team = Team.find_by_name(team_name)
        raise TeamNotFoundError unless selected_team

        logger.info "Looking for folder #{folder_name}..."
        selected_folder = selected_team.folder_by_name(folder_name)
        raise FolderNotFoundError unless selected_folder

        session_adapter.update_session({ folder: selected_folder.id })
        log_success "Selected folder #{folder_name.downcase} in team #{team_name.downcase}"
      end
    end
  end

  run!
end

Private Instance Methods

cryptopus_adapter() click to toggle source
# File lib/cli.rb, line 323
def cryptopus_adapter
  @cryptopus_adapter ||= CryptopusAdapter.new
end
execute_action(options = {}) { || ... } click to toggle source
# File lib/cli.rb, line 246
def execute_action(options = {})
  yield if block_given?
rescue SessionMissingError
  exit_with_error(:usage_error, 'Not logged in')
rescue UnauthorizedError
  exit_with_error(:usage_error, 'Authorization failed')
rescue ForbiddenError
  exit_with_error(:usage_error, 'Access denied')
rescue SocketError
  exit_with_error(:usage_error, 'Could not connect')
rescue NoFolderSelectedError
  exit_with_error(:usage_error, 'Folder must be selected using cry folder <id>')
rescue OpenshiftClientMissingError
  exit_with_error(:usage_error, 'oc is not installed')
rescue OpenshiftClientNotLoggedInError
  exit_with_error(:usage_error, 'oc is not logged in')
rescue KubernetesClientMissingError
  exit_with_error(:usage_error, 'kubectl is not installed')
rescue KubernetesClientNotLoggedInError
  exit_with_error(:usage_error, 'kubectl is not logged in')
rescue CryptopusAccountNotFoundError
  exit_with_error(:usage_error, 'Secret with the given name ' \
                                "#{options[:secret_name]} was not found")
rescue OpenshiftSecretNotFoundError
  exit_with_error(:usage_error, 'Secret with the given name ' \
                                "#{options[:secret_name]} was not found")
rescue TeamNotFoundError
  exit_with_error(:usage_error, 'Team with the given name ' \
                                "#{options[:team_name]} was not found")
rescue FolderNotFoundError
  exit_with_error(:usage_error, 'Folder with the given name ' \
                                "#{options[:folder_name]} was not found")
end
exit_with_error(error, msg) click to toggle source
# File lib/cli.rb, line 300
def exit_with_error(error, msg)
  logger = TTY::Logger.new do |config|
    config.output = $stderr
  end
  logger.error(msg)
  TTY::Exit.exit_with(error)
end
extract_login_args(args) click to toggle source

rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metric/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength

# File lib/cli.rb, line 282
def extract_login_args(args)
  exit_with_error(:usage_error, 'Credentials missing') if args.empty?
  token, url = args.first.split('@')
  exit_with_error(:usage_error, 'URL missing') unless url
  exit_with_error(:usage_error, 'Token missing') if token.empty?
  [token, url]
end
extract_use_args(args) click to toggle source
# File lib/cli.rb, line 290
def extract_use_args(args)
  usage_info = 'Usage: cry use <team/folder>'

  exit_with_error(:usage_error, "Arguments missing\n#{usage_info}") unless args.length >= 1
  team_name, folder_name = args.first.split('/').map(&:downcase)
  exit_with_error(:usage_error, "Team name is missing\n#{usage_info}") if team_name.empty?
  exit_with_error(:usage_error, "Folder name is missing\n#{usage_info}") unless folder_name
  [team_name, folder_name]
end
k8s_adapter() click to toggle source
# File lib/cli.rb, line 331
def k8s_adapter
  @k8s_adapter ||= K8SAdapter.new
end
log_success(msg) click to toggle source
# File lib/cli.rb, line 308
def log_success(msg)
  logger = TTY::Logger.new do |config|
    config.output = $stdout
  end
  logger.success msg
end
logger() click to toggle source
# File lib/cli.rb, line 315
def logger
  @logger ||= TTY::Logger.new
end
ose_adapter() click to toggle source
# File lib/cli.rb, line 319
def ose_adapter
  @ose_adapter ||= OSEAdapter.new
end
session_adapter() click to toggle source
# File lib/cli.rb, line 327
def session_adapter
  @session_adapter ||= SessionAdapter.new
end