Skip to content

Commit

Permalink
Generate GCP tokens through cmd-path config
Browse files Browse the repository at this point in the history
This commit pushes the `Kubeclient::GoogleApplicationDefaultCredentials`
implementation down to a `GCPAuthProvider` that handle both code paths of
how to generate GCP tokens.
  • Loading branch information
lucasmazza committed Apr 30, 2019
1 parent 9a3a941 commit 61479b9
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 10 deletions.
1 change: 1 addition & 0 deletions kubeclient.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'googleauth', '~> 0.5.1'
spec.add_development_dependency('mocha', '~> 1.5')
spec.add_development_dependency 'openid_connect', '~> 1.1'
spec.add_development_dependency 'jsonpath', '~> 1.0'

spec.add_dependency 'rest-client', '~> 2.0'
spec.add_dependency 'recursive-open-struct', '~> 1.0', '>= 1.0.4'
Expand Down
2 changes: 1 addition & 1 deletion lib/kubeclient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
require 'kubeclient/common'
require 'kubeclient/config'
require 'kubeclient/entity_list'
require 'kubeclient/google_application_default_credentials'
require 'kubeclient/exec_credentials'
require 'kubeclient/gcp_auth_provider'
require 'kubeclient/http_error'
require 'kubeclient/missing_kind_compatibility'
require 'kubeclient/oidc_auth_provider'
Expand Down
28 changes: 19 additions & 9 deletions lib/kubeclient/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,23 +150,33 @@ def fetch_user_auth_options(user)
if user.key?('token')
options[:bearer_token] = user['token']
elsif user.key?('exec')
exec_opts = user['exec'].dup
exec_opts['command'] = ext_command_path(exec_opts['command']) if exec_opts['command']
exec_opts = expand_command_option(user['exec'], 'command')
options[:bearer_token] = Kubeclient::ExecCredentials.token(exec_opts)
elsif user.key?('auth-provider')
auth_provider = user['auth-provider']
options[:bearer_token] = case auth_provider['name']
when 'gcp'
then Kubeclient::GoogleApplicationDefaultCredentials.token
when 'oidc'
then Kubeclient::OIDCAuthProvider.token(auth_provider['config'])
end
options[:bearer_token] = fetch_token_from_provider(user['auth-provider'])
else
%w[username password].each do |attr|
options[attr.to_sym] = user[attr] if user.key?(attr)
end
end
options
end

def fetch_token_from_provider(auth_provider)
case auth_provider['name']
when 'gcp'
config = expand_command_option(auth_provider['config'], 'cmd-path')
Kubeclient::GCPAuthProvider.token(config)
when 'oidc'
Kubeclient::OIDCAuthProvider.token(auth_provider['config'])
end
end

def expand_command_option(config, key)
config = config.dup
config[key] = ext_command_path(config[key]) if config[key]

config
end
end
end
19 changes: 19 additions & 0 deletions lib/kubeclient/gcp_auth_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

require 'kubeclient/google_application_default_credentials'
require 'kubeclient/gcp_command_credentials'

module Kubeclient
# Handle different ways to get a bearer token for Google Cloud Platform.
class GCPAuthProvider
class << self
def token(config)
if config.key?('cmd-path')
Kubeclient::GCPCommandCredentials.token(config)
else
Kubeclient::GoogleApplicationDefaultCredentials.token
end
end
end
end
end
31 changes: 31 additions & 0 deletions lib/kubeclient/gcp_command_credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Kubeclient
# Generates a bearer token for Google Cloud Platform.
class GCPCommandCredentials
class << self
def token(config)
require 'open3'
require 'shellwords'
require 'json'
require 'jsonpath'

cmd = config['cmd-path']
args = config['cmd-args']
token_key = config['token-key']

out, err, st = Open3.capture3(cmd, *args.split(' '))

raise "exec command failed: #{err}" unless st.success?

extract_token(out, token_key)
end

private

def extract_token(output, token_key)
JsonPath.on(output, token_key.gsub(/^{|}$/, '')).first
end
end
end
end
26 changes: 26 additions & 0 deletions test/config/gcpcmdauth.kubeconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: v1
clusters:
- cluster:
server: https://localhost:8443
insecure-skip-tls-verify: true
name: localhost:8443
contexts:
- context:
cluster: localhost:8443
namespace: default
user: application-default-credentials
name: localhost/application-default-credentials
kind: Config
preferences: {}
users:
- name: application-default-credentials
user:
auth-provider:
config:
access-token: <fake_token>
cmd-args: config config-helper --format=json
cmd-path: /path/to/gcloud
expiry: 2019-04-09T19:26:18Z
expiry-key: '{.credential.token_expiry}'
token-key: '{.credential.access_token}'
name: gcp
14 changes: 14 additions & 0 deletions test/test_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,20 @@ def test_gcp_default_auth_renew
assert_equal({ bearer_token: 'token1' }, context.auth_options)
end

def test_gcp_command_auth
Kubeclient::GCPCommandCredentials.expects(:token)
.with('access-token' => '<fake_token>',
'cmd-args' => 'config config-helper --format=json',
'cmd-path' => '/path/to/gcloud',
'expiry' => '2019-04-09 19:26:18 UTC',
'expiry-key' => '{.credential.token_expiry}',
'token-key' => '{.credential.access_token}')
.returns('token1')
.once
config = Kubeclient::Config.read(config_file('gcpcmdauth.kubeconfig'))
config.context(config.contexts.first)
end

def test_oidc_auth_provider
Kubeclient::OIDCAuthProvider.expects(:token)
.with('client-id' => 'fake-client-id',
Expand Down
27 changes: 27 additions & 0 deletions test/test_gcp_command_credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require_relative 'test_helper'
require 'open3'

# Unit tests for the GCPCommandCredentials token provider
class GCPCommandCredentialsTest < MiniTest::Test
def test_token
opts = { 'cmd-args' => 'config config-helper --format=json',
'cmd-path' => '/path/to/gcloud',
'expiry-key' => '{.credential.token_expiry}',
'token-key' => '{.credential.access_token}' }

creds = JSON.dump(
'credential' => {
'access_token' => '9A3A941836F2458175BE18AA1971EBBF47949B07',
'token_expiry' => '2019-04-12T15:02:51Z'
}
)

st = Minitest::Mock.new
st.expect(:success?, true)

Open3.stub(:capture3, [creds, nil, st]) do
assert_equal('9A3A941836F2458175BE18AA1971EBBF47949B07',
Kubeclient::GCPCommandCredentials.token(opts))
end
end
end

0 comments on commit 61479b9

Please sign in to comment.