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

Initial import of transport for GCP. #283

Merged
merged 7 commits into from
May 14, 2018
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: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ group :test do
end

group :integration do
gem 'berkshelf', '~> 4.3'
gem 'berkshelf', '~> 5.2'
gem 'test-kitchen', '~> 1.11'
gem 'kitchen-vagrant'
end
Expand Down
1 change: 1 addition & 0 deletions lib/train/platforms/detect/specifications/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def self.load
plat.family('cloud').in_family('api')
plat.name('aws').in_family('cloud')
plat.name('azure').in_family('cloud')
plat.name('gcp').in_family('cloud')
end
end
end
92 changes: 92 additions & 0 deletions lib/train/transports/gcp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# encoding: utf-8

require 'train/plugins'
require 'google/apis'
require 'google/apis/cloudresourcemanager_v1'
require 'google/apis/compute_v1'
require 'google/apis/storage_v1'
require 'google/apis/iam_v1'
require 'googleauth'

module Train::Transports
class Gcp < Train.plugin(1)
name 'gcp'

# GCP will look automatically for the below env var for service accounts etc. :
option :google_application_credentials, required: false, default: ENV['GOOGLE_APPLICATION_CREDENTIALS']
# see https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
# In the absence of this, the client is expected to have already set up local credentials via:
# $ gcloud auth application-default login
# $ gcloud config set project <project-name>
# GCP projects can have default regions / zones set, see:
# https://cloud.google.com/compute/docs/regions-zones/changing-default-zone-region
# can also specify project via env var:
option :google_cloud_project, required: false, default: ENV['GOOGLE_CLOUD_PROJECT']

def connection(_ = nil)
@connection ||= Connection.new(@options)
end

class Connection < BaseConnection
def initialize(options)
super(options)

# additional GCP platform metadata
release = Gem.loaded_specs['google_cloud']
@platform_details = { release: "google-cloud-v#{release}" }

# Initialize the client object cache
@cache_enabled[:api_call] = true
@cache[:api_call] = {}

connect
end

def platform
direct_platform('gcp', @platform_details)
end

# Instantiate some named classes for ease of use
def gcp_compute_client
Copy link
Contributor

Choose a reason for hiding this comment

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

if your going to make helper methods can you use the gcp_client for them all? That way the logic is in one spot.

def gcp_iam_client
   gcp_client(Google::Apis::IamV1::IamService)
end

Copy link
Author

Choose a reason for hiding this comment

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

Good shout! I'll update that now.

gcp_client(Google::Apis::ComputeV1::ComputeService)
end

def gcp_iam_client
gcp_client(Google::Apis::IamV1::IamService)
end

def gcp_project_client
gcp_client(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService)
end

def gcp_storage_client
gcp_client(Google::Apis::StorageV1::StorageService)
end

# Let's allow for other clients too
def gcp_client(klass)
return klass.new unless cache_enabled?(:api_call)
@cache[:api_call][klass.to_s.to_sym] ||= klass.new
end

def connect
ENV['GOOGLE_APPLICATION_CREDENTIALS'] = @options[:google_application_credentials] if @options[:google_application_credentials]
ENV['GOOGLE_CLOUD_PROJECT'] = @options[:google_cloud_project] if @options[:google_cloud_project]
# GCP initialization
scopes = ['https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/compute']
authorization = Google::Auth.get_application_default(scopes)
Google::Apis::RequestOptions.default.authorization = authorization
end

def uri
"gcp://#{unique_identifier}"
end

def unique_identifier
# use auth client_id - same to retrieve for any of the clients but use IAM
gcp_iam_client.request_options.authorization.client_id
end
end
end
end
191 changes: 191 additions & 0 deletions test/unit/transports/gcp_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# encoding: utf-8

require 'helper'

describe 'gcp transport' do

let(:credentials_file) do
require 'tempfile'
file = Tempfile.new('application_default_credentials.json')
info = <<-INFO
{
"client_id": "asdfasf-asdfasdf.apps.googleusercontent.com",
"client_secret": "d-asdfasdf",
"refresh_token": "1/adsfasdf-lCkju3-yQmjr20xVZonrfkE48L",
"type": "authorized_user"
}
INFO
file.write(info)
file.close
file
end

let(:credentials_file_override) do
require 'tempfile'
file = Tempfile.new('application_default_credentials.json')
info = <<-INFO
{
"client_id": "asdfasf-asdfasdf.apps.googleusercontent.com",
"client_secret": "d-asdfasdf",
"refresh_token": "1/adsfasdf-lCkju3-yQmjr20xVZonrfkE48L",
"type": "authorized_user"
}
INFO
file.write(info)
file.close
file
end

def transport(options = nil)
ENV['GOOGLE_APPLICATION_CREDENTIALS'] = credentials_file.path
ENV['GOOGLE_CLOUD_PROJECT'] = 'test_project'
# need to require this at here as it captures the envs on load
require 'train/transports/gcp'
Train::Transports::Gcp.new(options)
end

let(:connection) { transport.connection }
let(:options) { connection.instance_variable_get(:@options) }
let(:cache) { connection.instance_variable_get(:@cache) }

describe 'options' do
it 'defaults to env options' do
options[:google_application_credentials] = credentials_file.path
options[:google_cloud_project].must_equal 'test_project'
end
end

it 'allows for options override' do
transport = transport(google_application_credentials: credentials_file_override.path, google_cloud_project: "override_project")
options = transport.connection.instance_variable_get(:@options)
options[:google_application_credentials].must_equal credentials_file_override.path
options[:google_cloud_project].must_equal "override_project"
end

describe 'platform' do
it 'returns platform' do
platform = connection.platform
platform.name.must_equal 'gcp'
platform.family_hierarchy.must_equal ['cloud', 'api']
end
end

describe 'gcp_client' do
it 'test gcp_client with caching' do
client = connection.gcp_client(Object)
client.is_a?(Object).must_equal true
cache[:api_call].count.must_equal 1
end

it 'test gcp_client without caching' do
connection.disable_cache(:api_call)
client = connection.gcp_client(Object)
client.is_a?(Object).must_equal true
cache[:api_call].count.must_equal 0
end
end


describe 'gcp_compute_client' do
it 'test gcp_compute_client with caching' do
client = connection.gcp_compute_client
client.is_a?(Google::Apis::ComputeV1::ComputeService).must_equal true
cache[:api_call].count.must_equal 1
end

it 'test gcp_client without caching' do
connection.disable_cache(:api_call)
client = connection.gcp_compute_client
client.is_a?(Google::Apis::ComputeV1::ComputeService).must_equal true
cache[:api_call].count.must_equal 0
end
end

describe 'gcp_iam_client' do
it 'test gcp_iam_client with caching' do
client = connection.gcp_iam_client
client.is_a?(Google::Apis::IamV1::IamService).must_equal true
cache[:api_call].count.must_equal 1
end

it 'test gcp_iam_client without caching' do
connection.disable_cache(:api_call)
client = connection.gcp_iam_client
client.is_a?(Google::Apis::IamV1::IamService).must_equal true
cache[:api_call].count.must_equal 0
end
end

describe 'gcp_project_client' do
it 'test gcp_project_client with caching' do
client = connection.gcp_project_client
client.is_a?(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService).must_equal true
cache[:api_call].count.must_equal 1
end

it 'test gcp_project_client without caching' do
connection.disable_cache(:api_call)
client = connection.gcp_project_client
client.is_a?(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService).must_equal true
cache[:api_call].count.must_equal 0
end
end

describe 'gcp_storage_client' do
it 'test gcp_storage_client with caching' do
client = connection.gcp_storage_client
client.is_a?(Google::Apis::StorageV1::StorageService).must_equal true
cache[:api_call].count.must_equal 1
end

it 'test gcp_storage_client without caching' do
connection.disable_cache(:api_call)
client = connection.gcp_storage_client
client.is_a?(Google::Apis::StorageV1::StorageService).must_equal true
cache[:api_call].count.must_equal 0
end
end

# test options override of env vars in connect
describe 'connect' do
let(:creds) do
require 'tempfile'
file = Tempfile.new('creds')
info = <<-INFO
{
"client_id": "asdfasf-asdfasdf.apps.googleusercontent.com",
"client_secret": "d-asdfasdf",
"refresh_token": "1/adsfasdf-lCkju3-yQmjr20xVZonrfkE48L",
"type": "authorized_user"
}
INFO
file.write(info)
file.close
file
end
it 'validate gcp connection with credentials' do
options[:google_application_credentials] = creds.path
connection.connect
ENV['GOOGLE_APPLICATION_CREDENTIALS'].must_equal creds.path
end
it 'validate gcp connection with project' do
options[:google_cloud_project] = 'project'
connection.connect
ENV['GOOGLE_CLOUD_PROJECT'].must_equal 'project'
end
end

describe 'unique_identifier' do
it 'test connection unique identifier' do
client = connection
client.unique_identifier.must_equal 'asdfasf-asdfasdf.apps.googleusercontent.com'
end
end

describe 'uri' do
it 'test uri' do
client = connection
client.uri.must_equal 'gcp://asdfasf-asdfasdf.apps.googleusercontent.com'
end
end
end
4 changes: 4 additions & 0 deletions train.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Gem::Specification.new do |spec|
spec.add_dependency 'docker-api', '~> 1.26'
spec.add_dependency 'aws-sdk', '~> 2'
spec.add_dependency 'azure_mgmt_resources', '~> 0.15'
spec.add_dependency 'google-api-client', '~> 0.19.8'
spec.add_dependency 'googleauth', '~> 0.6.2'
spec.add_dependency 'google-cloud', '~> 0.51.1'
spec.add_dependency 'google-protobuf', '= 3.5.1'
spec.add_dependency 'inifile'

spec.add_development_dependency 'mocha', '~> 1.1'
Expand Down