Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add google cloud configuration object for kubeclient #88

Merged
merged 1 commit into from
May 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions kubernetes-deploy.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]
spec.add_dependency "activesupport", ">= 4.2"
spec.add_dependency "kubeclient", "~> 2.3"
spec.add_dependency "googleauth"

spec.add_development_dependency "bundler", "~> 1.13"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency "minitest-stub-const", "~> 0.6"
spec.add_development_dependency "webmock", "~> 3.0"
end
3 changes: 2 additions & 1 deletion lib/kubernetes-deploy/kubeclient_builder.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require 'kubeclient'
require 'kubernetes-deploy/kubeclient_builder/google_friendly_config'

module KubernetesDeploy
module KubeclientBuilder
Expand Down Expand Up @@ -28,7 +29,7 @@ def build_v1beta1_kubeclient(context)
end

def _build_kubeclient(api_version:, context:, endpoint_path: nil)
config = Kubeclient::Config.read(ENV.fetch("KUBECONFIG"))
config = GoogleFriendlyConfig.read(ENV.fetch("KUBECONFIG"))
unless config.contexts.include?(context)
raise ContextMissingError, context
end
Expand Down
44 changes: 44 additions & 0 deletions lib/kubernetes-deploy/kubeclient_builder/google_friendly_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

require 'googleauth'
module KubernetesDeploy
module KubeclientBuilder
class GoogleFriendlyConfig < Kubeclient::Config
def fetch_user_auth_options(user)
if user['auth-provider'] && (user['auth-provider']['name'] == 'gcp')
{ bearer_token: new_token }
else
super
end
end

def self.read(filename)
new(YAML.load_file(filename), File.dirname(filename))
end

def new_token
scopes = ['https://www.googleapis.com/auth/cloud-platform']
authorization = Google::Auth.get_application_default(scopes)

authorization.apply({})

authorization.access_token

rescue Signet::AuthorizationError => e
err_message = json_error_message(e.response.body) || e.message
raise KubeException.new(e.response.status, err_message, e.response.body)
end

private

def json_error_message(body)
json_error_msg = begin
JSON.parse(body || '') || {}
rescue JSON::ParserError
{}
end
json_error_msg['message']
end
end
end
end
3 changes: 3 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require 'timecop'
require 'minitest/autorun'
require 'minitest/stub/const'
require 'webmock/minitest'

require 'helpers/kubeclient_helper'
require 'helpers/fixture_deploy_helper'
Expand All @@ -14,6 +15,8 @@

ENV["KUBECONFIG"] ||= "#{Dir.home}/.kube/config"

WebMock.allow_net_connect!

module KubernetesDeploy
class TestCase < ::Minitest::Test
def setup
Expand Down
107 changes: 107 additions & 0 deletions test/unit/kubernetes-deploy/google_friendly_config_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# frozen_string_literal: true
require 'test_helper'

class GoogleFriendlyConfigTest < KubernetesDeploy::TestCase
def setup
WebMock.disable_net_connect!
set_google_env_vars
end

def teardown
WebMock.allow_net_connect!
end

def test_auth_use_default_gcp_success
config = KubernetesDeploy::KubeclientBuilder::GoogleFriendlyConfig.new(kubeconfig, "")

stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
.to_return(
headers: { 'Content-Type' => 'application/json' },
body: {
"access_token": "bearer_token",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "identity_token"
}.to_json,
status: 200
)

context = config.context("google")
assert_equal 'bearer_token', context.auth_options[:bearer_token]
end

def test_auth_use_default_gcp_failure
config = KubernetesDeploy::KubeclientBuilder::GoogleFriendlyConfig.new(kubeconfig, "")

stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
.to_return(
headers: { 'Content-Type' => 'application/json' },
body: '',
status: 401
)

assert_raises(KubeException) do
config.context("google")
end
end

def test_non_google_auth_works
config = KubernetesDeploy::KubeclientBuilder::GoogleFriendlyConfig.new(kubeconfig, "")

context = config.context("minikube")

assert_equal 'test', context.auth_options[:password]
assert_equal 'admin', context.auth_options[:username]
end

def kubeconfig
{
'apiVersion' => 'v1',
'clusters' => [
{ 'cluster' => { 'server' => 'https://192.168.64.3:8443' }, 'name' => 'test' }
],
'contexts' => [
{
'context' => {
'cluster' => 'test',
'user' => 'google'
},
'name' => 'google'
},
{
'context' => {
'cluster' => 'test', 'user' => 'minikube'
},
'name' => 'minikube'
},
],
'users' => [
{
'name' => 'google',
'user' => {
'auth-provider' => {
'name' => 'gcp',
'config' => { 'access_token' => 'test' }
}
}
},
{
'name' => 'minikube',
'user' => {
'password' => 'test',
'username' => 'admin'
}
}
]
}.stringify_keys
end

def set_google_env_vars
ENV["GOOGLE_PRIVATE_KEY"] ||= "FAKE"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's probably a reason why it's necessary, but I'd prefer them to be reset on teardown

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its because the googleauth gem does a bunch of magic inspection to determine how to load configuration for a host (ie checking if creds are locally stored on disk, whether this is a GCE host, etc...).

ENV["GOOGLE_CLIENT_EMAIL"] ||= "fake@email.com"
ENV["GOOGLE_ACCOUNT_TYPE"] ||= 'authorized_user'
ENV["GOOGLE_CLIENT_ID"] ||= 'fake'
ENV["GOOGLE_CLIENT_SECRET"] ||= 'fake'
ENV["REFRESH_TOKEN_VAR"] ||= 'fake'
end
end