Skip to content
Snippets Groups Projects
Commit 480dc210 authored by Mayra Cabrera's avatar Mayra Cabrera
Browse files

Integrates Kubernetes namespace on Clusters flow

- Modifies 'FinalizeCreationService' class to create KubernetesNamespace
  instance.
- Adds CreateServicesAccountService to ensure gitlab and namespaced
  services account are created.
- If cluster is using RBAC as authorization, also create a role binding
  and a cluster role binding account
parent ef5165ac
No related branches found
No related tags found
No related merge requests found
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20181009205043_populate_cluster_kubernetes_namespace.rb')
describe PopulateClusterKubernetesNamespace, :sidekiq, :migration do
let(:table_namespaces) { table(:namespaces) }
let(:table_projects) { table(:projects) }
let(:table_clusters) { table(:clusters) }
let(:table_platform_kubernetes) { table(:cluster_platforms_kubernetes) }
let(:table_cluster_projects) { table(:cluster_projects) }
let(:table_cluster_kubernetes_namespaces) { table(:clusters_kubernetes_namespaces) }
let(:namespace) { table_namespaces.create!(name: 'gitlab', path: 'gitlab_namespace') }
let(:projects) do
(1..10).each_with_object([]) do |n, array|
project = table_projects.create(id: n, name: "project_#{n}", path: "gitlab_#{n}", namespace_id: namespace.id)
array << project
end
end
describe '#up' do
before do
projects.each do |project|
create_cluster_structure(project)
end
end
it 'schedules delayed background migrations in batches' do
Sidekiq::Testing.fake! do
Timecop.freeze do
expect(Clusters::KubernetesNamespace.count).to eq(0)
migrate!
first_cluster_project = Project.first.cluster_project
last_cluster_project = Project.last.cluster_project
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, first_cluster_project.id, last_cluster_project.id)
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
end
end
end
def create_cluster_structure(project, kubernetes_namespace: false)
cluster = table_clusters.create!(name: "cluster_#{project.id}", platform_type: :kubernetes, provider_type: :user)
table_platform_kubernetes.create!(api_url: 'https://sample.kubernetes.com', ca_cert: 'ca_pem_sample', encrypted_token: 'token_sample', cluster_id: cluster.id)
table_cluster_projects.create!(cluster_id: cluster.id, project_id: project.id)
end
end
Loading
Loading
@@ -3,15 +3,40 @@
require 'spec_helper'
 
RSpec.describe Clusters::KubernetesNamespace, type: :model do
it { should belong_to(:cluster_project) }
it { should have_one(:project) }
it { should have_one(:cluster) }
it { is_expected.to belong_to(:cluster_project) }
it { is_expected.to have_one(:project) }
it { is_expected.to have_one(:cluster) }
describe 'namespace uniqueness validation' do
let(:platform) { create(:cluster_platform_kubernetes, namespace: 'my-namespace') }
let(:cluster) { platform.cluster }
let(:cluster_project) { create(:cluster_project, cluster: cluster) }
let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace, cluster_project: cluster_project) }
subject { kubernetes_namespace }
context 'when cluster project is using the namespace' do
before do
create(:cluster_kubernetes_namespace, cluster_project: cluster_project)
end
it { is_expected.not_to be_valid }
end
context 'when cluster project is not using the namespace' do
it { is_expected.to be_valid }
end
end
 
describe '#set_cluster_namespace_and_service_account' do
let(:cluster) { platform.cluster }
let(:cluster_project) { create(:cluster_project, cluster: cluster) }
let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace, cluster_project: cluster_project) }
 
before do
kubernetes_namespace.save
end
describe '#namespace' do
let(:platform) { create(:cluster_platform_kubernetes, namespace: namespace) }
 
Loading
Loading
@@ -21,8 +46,6 @@ RSpec.describe Clusters::KubernetesNamespace, type: :model do
let(:namespace) { 'my-own-namespace' }
 
it 'should copy the namespace' do
kubernetes_namespace.save
is_expected.to eq('my-own-namespace')
end
end
Loading
Loading
@@ -31,64 +54,20 @@ RSpec.describe Clusters::KubernetesNamespace, type: :model do
let(:namespace) { nil }
 
it 'should set default namespace' do
kubernetes_namespace.save
project_slug = "#{cluster_project.project.path}-#{cluster_project.project_id}"
is_expected.to eq(project_slug)
end
end
end
describe '#service_account_namespace' do
subject { kubernetes_namespace.service_account_name }
context 'when cluster is not using RBAC' do
let(:platform) { create(:cluster_platform_kubernetes) }
it 'should set default service account name' do
kubernetes_namespace.save
is_expected.to eq('gitlab')
end
end
 
context 'when cluster is using RBAC' do
let(:platform) { create(:cluster_platform_kubernetes, :rbac_enabled) }
it 'should set a service account name based on namespace' do
kubernetes_namespace.save
is_expected.to eq("gitlab-#{kubernetes_namespace.namespace}")
is_expected.to eq(project_slug)
end
end
end
end
describe '#ensure_namespace_uniqueness' do
let(:platform) { create(:cluster_platform_kubernetes) }
let(:cluster) { platform.cluster }
let(:cluster_project) { create(:cluster_project, cluster: cluster) }
let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace, cluster_project: cluster_project) }
subject { kubernetes_namespace }
context 'when cluster does not have the kubernetes namespace' do
it { is_expected.to be_valid }
end
 
context 'when cluster has the same kubernetes namespace' do
before do
create(:cluster_kubernetes_namespace,
namespace: 'my-namespace',
cluster_project: cluster_project)
end
describe '#service_account_name' do
let(:platform) { create(:cluster_platform_kubernetes) }
 
it { is_expected.to_not be_valid }
it 'should return an error on namespace' do
subject.save
subject { kubernetes_namespace.service_account_name }
 
project_slug = "#{cluster_project.project.path}-#{cluster_project.project_id}"
expect(subject.errors[:namespace].first).to eq("Kubernetes namespace #{project_slug} already exists on cluster")
it 'should set a service account name based on namespace' do
is_expected.to eq("#{kubernetes_namespace.namespace}-service-account")
end
end
end
Loading
Loading
Loading
Loading
@@ -91,8 +91,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
end
 
describe '#prevent_reserved_namespaces' do
describe 'when using reserved namespaces' do
subject { build(:cluster_platform_kubernetes, namespace: namespace) }
 
context 'when no namespace is manually assigned' do
Loading
Loading
@@ -110,7 +109,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
context 'when reserved namespace is assigned' do
let(:namespace) { 'gitlab-managed-apps' }
 
it { is_expected.to_not be_valid }
it { is_expected.not_to be_valid }
end
end
end
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
 
describe Clusters::Gcp::FinalizeCreationService do
describe Clusters::Gcp::FinalizeCreationService, '#execute' do
include GoogleApi::CloudPlatformHelpers
include KubernetesHelpers
 
describe '#execute' do
let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
let(:provider) { cluster.provider }
let(:platform) { cluster.platform }
let(:gcp_project_id) { provider.gcp_project_id }
let(:zone) { provider.zone }
let(:cluster_name) { cluster.name }
let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
let(:provider) { cluster.provider }
let(:platform) { cluster.platform }
let(:endpoint) { '111.111.111.111' }
let(:api_url) { 'https://' + endpoint }
let(:username) { 'sample-username' }
let(:password) { 'sample-password' }
let(:secret_name) { 'gitlab-token' }
let(:token) { 'sample-token' }
let(:namespace) { "#{cluster.project.path}-#{cluster.project.id}" }
 
subject { described_class.new.execute(provider) }
subject { described_class.new.execute(provider) }
 
shared_examples 'success' do
it 'configures provider and kubernetes' do
subject
shared_examples 'success' do
it 'configures provider and kubernetes' do
subject
 
expect(provider).to be_created
end
expect(provider).to be_created
end
 
it 'configures kubernetes platform' do
expect(ClusterPlatformConfigureWorker).to receive(:perform_async).with(cluster.id)
it 'properly configures database models' do
subject
 
subject
end
cluster.reload
expect(provider.endpoint).to eq(endpoint)
expect(platform.api_url).to eq(api_url)
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
expect(platform.username).to eq(username)
expect(platform.password).to eq(password)
expect(platform.token).to eq(token)
kubernetes_namespace = cluster.cluster_projects.first.kubernetes_namespace
expect(kubernetes_namespace).to be_persisted
expect(kubernetes_namespace.namespace).to eq(namespace)
expect(kubernetes_namespace.service_account_name).to eq("#{namespace}-service-account")
end
end
 
shared_examples 'error' do
it 'sets an error to provider object' do
subject
shared_examples 'error' do
it 'sets an error to provider object' do
subject
 
expect(provider.reload).to be_errored
expect(provider.reload).to be_errored
end
end
shared_examples 'kubernetes information not successfully fetched' do
context 'when failed to fetch gke cluster info' do
before do
stub_cloud_platform_get_zone_cluster_error(provider.gcp_project_id, provider.zone, cluster.name)
end
it_behaves_like 'error'
end
 
context 'when suceeded to fetch gke cluster info' do
let(:endpoint) { '111.111.111.111' }
let(:api_url) { 'https://' + endpoint }
let(:username) { 'sample-username' }
let(:password) { 'sample-password' }
let(:secret_name) { 'gitlab-token' }
context 'when token is empty' do
let(:token) { '' }
it_behaves_like 'error'
end
 
context 'when failed to fetch kubernetes token' do
before do
allow(ClusterPlatformConfigureWorker).to receive(:perform_async)
stub_cloud_platform_get_zone_cluster(
gcp_project_id, zone, cluster_name,
{
endpoint: endpoint,
username: username,
password: password
}
)
stub_kubeclient_get_secret_error(api_url, secret_name, namespace: 'default')
end
 
context 'service account and token created' do
before do
stub_kubeclient_discover(api_url)
stub_kubeclient_create_service_account(api_url)
stub_kubeclient_create_secret(api_url)
end
shared_context 'kubernetes token successfully fetched' do
let(:token) { 'sample-token' }
before do
stub_kubeclient_get_secret(
api_url,
{
metadata_name: secret_name,
token: Base64.encode64(token)
} )
end
end
context 'provider legacy_abac is enabled' do
include_context 'kubernetes token successfully fetched'
it_behaves_like 'success'
it 'properly configures database models' do
subject
cluster.reload
expect(provider.endpoint).to eq(endpoint)
expect(platform.api_url).to eq(api_url)
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
expect(platform.username).to eq(username)
expect(platform.password).to eq(password)
expect(platform).to be_abac
expect(platform.authorization_type).to eq('abac')
expect(platform.token).to eq(token)
end
end
context 'provider legacy_abac is disabled' do
before do
provider.legacy_abac = false
end
include_context 'kubernetes token successfully fetched'
context 'cluster role binding created' do
before do
stub_kubeclient_create_cluster_role_binding(api_url)
end
it_behaves_like 'success'
it 'properly configures database models' do
subject
cluster.reload
expect(provider.endpoint).to eq(endpoint)
expect(platform.api_url).to eq(api_url)
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
expect(platform.username).to eq(username)
expect(platform.password).to eq(password)
expect(platform).to be_rbac
expect(platform.token).to eq(token)
end
end
end
context 'when token is empty' do
before do
stub_kubeclient_get_secret(api_url, token: '', metadata_name: secret_name)
end
it_behaves_like 'error'
end
context 'when failed to fetch kubernetes token' do
before do
stub_kubeclient_get_secret_error(api_url, secret_name)
end
it_behaves_like 'error'
end
context 'when service account fails to create' do
before do
stub_kubeclient_create_service_account_error(api_url)
end
it_behaves_like 'error'
end
end
it_behaves_like 'error'
end
 
context 'when failed to fetch gke cluster info' do
context 'when service account fails to create' do
before do
stub_cloud_platform_get_zone_cluster_error(gcp_project_id, zone, cluster_name)
stub_kubeclient_create_service_account_error(api_url, namespace: namespace)
end
 
it_behaves_like 'error'
end
end
shared_context 'kubernetes information successfully fetched' do
before do
stub_cloud_platform_get_zone_cluster(
provider.gcp_project_id, provider.zone, cluster.name,
{
endpoint: endpoint,
username: username,
password: password
}
)
stub_kubeclient_discover(api_url)
stub_kubeclient_get_namespace(api_url)
stub_kubeclient_create_namespace(api_url)
stub_kubeclient_create_service_account(api_url)
stub_kubeclient_create_secret(api_url)
stub_kubeclient_get_secret(
api_url,
{
metadata_name: secret_name,
token: Base64.encode64(token),
namespace: 'default'
}
)
stub_kubeclient_get_namespace(api_url, namespace: namespace)
stub_kubeclient_create_service_account(api_url, namespace: namespace)
stub_kubeclient_create_secret(api_url, namespace: namespace)
stub_kubeclient_get_secret(
api_url,
{
metadata_name: "#{namespace}-token",
token: Base64.encode64(token),
namespace: namespace
}
)
end
end
context 'With a legacy ABAC cluster' do
before do
provider.legacy_abac = true
end
include_context 'kubernetes information successfully fetched'
it_behaves_like 'success'
it 'uses ABAC authorization type' do
subject
cluster.reload
expect(platform).to be_abac
expect(platform.authorization_type).to eq('abac')
end
it_behaves_like 'kubernetes information not successfully fetched'
end
context 'With an RBAC cluster' do
before do
provider.legacy_abac = false
stub_kubeclient_create_cluster_role_binding(api_url)
stub_kubeclient_create_role_binding(api_url, namespace: namespace)
end
include_context 'kubernetes information successfully fetched'
it_behaves_like 'success'
it 'uses RBAC authorization type' do
subject
cluster.reload
expect(platform).to be_rbac
expect(platform.authorization_type).to eq('rbac')
end
it_behaves_like 'kubernetes information not successfully fetched'
end
end
Loading
Loading
@@ -2,94 +2,153 @@
 
require 'spec_helper'
 
describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
describe Clusters::Gcp::Kubernetes::CreateServiceAccountService, '#execute' do
include KubernetesHelpers
 
let(:service) { described_class.new(kubeclient, rbac: rbac) }
let(:service) { described_class.new(kubeclient, name: service_account_name, namespace: namespace, rbac: rbac) }
let(:api_url) { 'http://111.111.111.111' }
let(:platform_kubernetes) { cluster.platform_kubernetes }
let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster_project: cluster.cluster_projects.first) }
let(:namespace) { kubernetes_namespace.namespace }
 
describe '#execute' do
let(:rbac) { false }
let(:api_url) { 'http://111.111.111.111' }
let(:username) { 'admin' }
let(:password) { 'xxx' }
let(:kubeclient) do
Gitlab::Kubernetes::KubeClient.new(
api_url,
['api', 'apis/rbac.authorization.k8s.io'],
auth_options: { username: username, password: password }
let(:cluster) do
create(:cluster,
:project, :provided_by_gcp,
platform_kubernetes: create(:cluster_platform_kubernetes, :configured))
end
let(:kubeclient) do
Gitlab::Kubernetes::KubeClient.new(
api_url,
['api', 'apis/rbac.authorization.k8s.io'],
auth_options: { username: 'admin', password: 'password' }
)
end
subject { service.execute }
before do
stub_kubeclient_discover(api_url)
stub_kubeclient_get_namespace(api_url, namespace: namespace)
stub_kubeclient_create_service_account(api_url, namespace: namespace )
stub_kubeclient_create_secret(api_url, namespace: namespace)
end
shared_examples 'creates service account and token' do
it 'creates a kubernetes service account' do
subject
expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts").with(
body: hash_including(
kind: 'ServiceAccount',
metadata: { name: service_account_name, namespace: namespace }
)
)
end
it 'creates a kubernetes secret' do
subject
expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets").with(
body: hash_including(
kind: 'Secret',
metadata: {
name: token_name,
namespace: namespace,
annotations: {
'kubernetes.io/service-account.name': service_account_name
}
},
type: 'kubernetes.io/service-account-token'
)
)
end
end
context 'With ABAC cluster' do
let(:service_account_name) { 'gitlab' }
let(:namespace) { 'default' }
let(:rbac) { false }
let(:token_name) { 'gitlab-token' }
 
subject { service.execute }
it_behaves_like 'creates service account and token'
end
context 'With RBAC enabled cluster' do
let(:rbac) { true }
before do
cluster.platform_kubernetes.rbac!
end
context 'when creating default namespace' do
let(:service_account_name) { 'gitlab' }
let(:namespace) { 'default' }
let(:token_name) { 'gitlab-token' }
 
context 'when params are correct' do
before do
stub_kubeclient_discover(api_url)
stub_kubeclient_create_service_account(api_url)
stub_kubeclient_create_secret(api_url)
stub_kubeclient_create_cluster_role_binding(api_url)
end
 
shared_examples 'creates service account and token' do
it 'creates a kubernetes service account' do
subject
expect(WebMock).to have_requested(:post, api_url + '/api/v1/namespaces/default/serviceaccounts').with(
body: hash_including(
kind: 'ServiceAccount',
metadata: { name: 'gitlab', namespace: 'default' }
)
)
end
it 'creates a kubernetes secret of type ServiceAccountToken' do
subject
expect(WebMock).to have_requested(:post, api_url + '/api/v1/namespaces/default/secrets').with(
body: hash_including(
kind: 'Secret',
metadata: {
name: 'gitlab-token',
namespace: 'default',
annotations: {
'kubernetes.io/service-account.name': 'gitlab'
}
},
type: 'kubernetes.io/service-account-token'
)
it_behaves_like 'creates service account and token'
it 'should create a cluster role binding with cluster-admin access' do
subject
expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings").with(
body: hash_including(
kind: 'ClusterRoleBinding',
metadata: { name: 'gitlab-admin' },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'ClusterRole',
name: 'cluster-admin'
},
subjects: [
{
kind: 'ServiceAccount',
name: service_account_name,
namespace: namespace
}
]
)
end
)
end
end
context 'when creating project namespace' do
let(:service_account_name) { "#{namespace}-service-account" }
let(:namespace) { "#{cluster.project.path}-#{cluster.project.id}" }
let(:token_name) { "#{namespace}-token" }
 
context 'abac enabled cluster' do
it_behaves_like 'creates service account and token'
before do
stub_kubeclient_create_role_binding(api_url, namespace: namespace)
end
 
context 'rbac enabled cluster' do
let(:rbac) { true }
before do
stub_kubeclient_create_cluster_role_binding(api_url)
end
it_behaves_like 'creates service account and token'
it 'creates a kubernetes cluster role binding' do
subject
expect(WebMock).to have_requested(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings').with(
body: hash_including(
kind: 'ClusterRoleBinding',
metadata: { name: 'gitlab-admin' },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'ClusterRole',
name: 'cluster-admin'
},
subjects: [{ kind: 'ServiceAccount', namespace: 'default', name: 'gitlab' }]
)
it_behaves_like 'creates service account and token'
it 'creates a namespaced role binding with edit access' do
subject
expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings").with(
body: hash_including(
kind: 'RoleBinding',
metadata: { name: "gitlab-#{namespace}", namespace: "#{namespace}" },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Role',
name: 'edit'
},
subjects: [
{
kind: 'ServiceAccount',
name: service_account_name,
namespace: namespace
}
]
)
end
)
end
end
end
Loading
Loading
# frozen_string_literal: true
 
require 'fast_spec_helper'
require 'spec_helper'
 
describe Clusters::Gcp::Kubernetes::FetchKubernetesTokenService do
include KubernetesHelpers
describe '#execute' do
let(:api_url) { 'http://111.111.111.111' }
let(:username) { 'admin' }
let(:password) { 'xxx' }
let(:namespace) { 'my-namespace' }
let(:service_account_token_name) { 'gitlab-token' }
 
let(:kubeclient) do
Gitlab::Kubernetes::KubeClient.new(
api_url,
['api', 'apis/rbac.authorization.k8s.io'],
auth_options: { username: username, password: password }
auth_options: { username: 'admin', password: 'xxx' }
)
end
 
subject { described_class.new(kubeclient).execute }
subject { described_class.new(kubeclient, service_account_token_name, namespace).execute }
 
context 'when params correct' do
let(:decoded_token) { 'xxx.token.xxx' }
let(:token) { Base64.encode64(decoded_token) }
 
let(:secret_json) do
{
'metadata': {
name: 'gitlab-token'
},
'data': {
'token': token
}
}
end
before do
allow_any_instance_of(Kubeclient::Client)
.to receive(:get_secret).and_return(secret_json)
end
context 'when gitlab-token exists' do
let(:metadata_name) { 'gitlab-token' }
before do
stub_kubeclient_discover(api_url)
stub_kubeclient_get_secret(
api_url,
{
metadata_name: service_account_token_name,
namespace: namespace,
token: token
}
)
end
 
it { is_expected.to eq(decoded_token) }
end
 
context 'when gitlab-token does not exist' do
let(:secret_json) { {} }
it { is_expected.to be_nil }
end
context 'when token is nil' do
let(:token) { nil }
before do
allow(kubeclient).to receive(:get_secret).and_raise(Kubeclient::HttpError.new(404, 'Not found', nil))
end
 
it { is_expected.to be_nil }
end
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe Clusters::Gcp::ServicesAccountService, '#execute' do
include GoogleApi::CloudPlatformHelpers
include KubernetesHelpers
let(:api_url) { 'https://111.111.111.111' }
let(:cluster) { create(:cluster, :project, :providing_by_gcp, platform_kubernetes: create(:cluster_platform_kubernetes)) }
let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster_project: cluster.cluster_projects.first) }
let(:namespace) { kubernetes_namespace.namespace }
let(:kubeclient) do
Gitlab::Kubernetes::KubeClient.new(
api_url,
['api', 'apis/rbac.authorization.k8s.io'],
auth_options: { username: 'sample-username', password: 'sample-password' }
)
end
subject { described_class.new(kubeclient, cluster).execute }
shared_context 'shared kubernetes requests' do
before do
stub_kubeclient_discover(api_url)
stub_kubeclient_get_namespace(api_url)
stub_kubeclient_create_service_account(api_url)
stub_kubeclient_create_secret(api_url)
stub_kubeclient_get_namespace(api_url, namespace: namespace)
stub_kubeclient_create_service_account(api_url, namespace: namespace)
stub_kubeclient_create_secret(api_url, namespace: namespace)
end
end
shared_examples 'creates default and namespaced services accounts' do
it 'creates default service account' do
subject
expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/default/serviceaccounts").with(
body: hash_including(
kind: 'ServiceAccount',
metadata: { name: 'gitlab', namespace: 'default' }
)
)
end
it 'creates a namespaced service account' do
subject
expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts").with(
body: hash_including(
kind: 'ServiceAccount',
metadata: { name: "#{namespace}-service-account", namespace: namespace }
)
)
end
it 'creates a default token' do
subject
expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/default/secrets").with(
body: hash_including(
kind: 'Secret',
metadata: {
name: 'gitlab-token',
namespace: 'default',
annotations: {
'kubernetes.io/service-account.name': 'gitlab'
}
}
)
)
end
it 'creates a restricted token' do
subject
expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets").with(
body: hash_including(
kind: 'Secret',
metadata: {
name: "#{namespace}-token",
namespace: namespace,
annotations: {
'kubernetes.io/service-account.name': "#{namespace}-service-account"
}
}
)
)
end
end
context 'With an ABAC cluster' do
include_context 'shared kubernetes requests'
it_behaves_like 'creates default and namespaced services accounts'
end
context 'With an RBAC cluster' do
include_context 'shared kubernetes requests'
before do
cluster.platform_kubernetes.rbac!
stub_kubeclient_create_cluster_role_binding(api_url)
stub_kubeclient_create_role_binding(api_url, namespace: namespace)
end
it_behaves_like 'creates default and namespaced services accounts'
it 'creates a cluster role binding with cluster-admin access' do
subject
expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings").with(
body: hash_including(
kind: 'ClusterRoleBinding',
metadata: { name: 'gitlab-admin' },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'ClusterRole',
name: 'cluster-admin'
},
subjects: [
{
kind: 'ServiceAccount',
name: 'gitlab',
namespace: 'default'
}
]
)
)
end
it 'creates a namespaced role binding with edit access' do
subject
expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings").with(
body: hash_including(
kind: 'RoleBinding',
metadata: { name: "gitlab-#{namespace}", namespace: "#{namespace}" },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Role',
name: 'edit'
},
subjects: [
{
kind: 'ServiceAccount',
name: "#{namespace}-service-account",
namespace: "#{namespace}"
}
]
)
)
end
end
end
Loading
Loading
@@ -5,31 +5,49 @@ require 'spec_helper'
describe Clusters::Kubernetes::ConfigureService, '#execute' do
include KubernetesHelpers
 
let(:cluster) { create(:cluster, :provided_by_gcp) }
let(:cluster_project) { create(:cluster_project, cluster: cluster) }
let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster_project: cluster_project) }
let(:kubeclient) { platform.kubeclient }
let(:platform) { kubernetes_namespace.cluster.platform_kubernetes }
let(:namespace) { "#{cluster_project.project.path}-#{cluster_project.project_id}" }
let(:service) do
described_class.new(platform)
end
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:platform) { cluster.platform }
let(:api_url) { 'https://kubernetes.example.com' }
let(:namespace) { "#{cluster.project.path}-#{cluster.project.id}" }
 
subject { service.execute }
subject { described_class.new(cluster).execute }
 
before do
api_url = 'https://kubernetes.example.com'
stub_kubeclient_discover(api_url)
stub_kubeclient_get_namespace(api_url)
stub_kubeclient_create_service_account(api_url)
stub_kubeclient_create_secret(api_url)
stub_kubeclient_get_namespace(api_url, namespace: namespace)
stub_kubeclient_create_namespace(api_url)
stub_kubeclient_create_service_account(api_url, namespace: namespace)
stub_kubeclient_create_secret(api_url, namespace: namespace)
stub_kubeclient_get_secret(
api_url,
{
metadata_name: "#{namespace}-token",
token: Base64.encode64('sample-token'),
namespace: namespace
}
)
end
 
it 'creates a kubernetes namespace' do
expect(kubeclient).to receive(:get_namespace).once.ordered
expect(kubeclient).to receive(:create_namespace).once.ordered
expect do
subject
end.to change(Clusters::KubernetesNamespace, :count).by(1)
end
it 'calls ServicesAccountService' do
expect_any_instance_of(Clusters::Gcp::ServicesAccountService).to receive(:execute).once
 
subject
end
it 'configures kubernetes token' do
subject
kubernetes_namespace = cluster.cluster_projects.first.kubernetes_namespace
expect(kubernetes_namespace.encrypted_service_account_token).to be_present
end
end
Loading
Loading
@@ -33,10 +33,11 @@ module KubernetesHelpers
WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response)
end
 
def stub_kubeclient_get_secret(api_url, namespace: 'default', **options)
def stub_kubeclient_get_secret(api_url, **options)
options[:metadata_name] ||= "default-token-1"
options[:namespace] ||= "default"
 
WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{options[:metadata_name]}")
WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}")
.to_return(kube_response(kube_v1_secret_body(options)))
end
 
Loading
Loading
@@ -65,6 +66,11 @@ module KubernetesHelpers
.to_return(kube_response({}))
end
 
def stub_kubeclient_create_role_binding(api_url, namespace: 'default')
WebMock.stub_request(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings")
.to_return(kube_response({}))
end
def stub_kubeclient_create_namespace(api_url)
WebMock.stub_request(:post, api_url + "/api/v1/namespaces")
.to_return(kube_response({}))
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment