# frozen_string_literal: true require 'spec_helper' RSpec.describe Gitlab::Kubernetes::Helm::API do let(:client) { double('kubernetes client') } let(:helm) { described_class.new(client) } let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE } let(:gitlab_namespace_labels) { Gitlab::Kubernetes::Helm::NAMESPACE_LABELS } let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client, labels: gitlab_namespace_labels) } let(:application_name) { 'app-name' } let(:rbac) { false } let(:files) { {} } let(:command) do Gitlab::Kubernetes::Helm::V2::InstallCommand.new( name: application_name, chart: 'chart-name', rbac: rbac, files: files ) end subject { helm } before do allow(Gitlab::Kubernetes::Namespace).to( receive(:new).with(gitlab_namespace, client, labels: gitlab_namespace_labels).and_return(namespace) ) allow(client).to receive(:create_config_map) end describe '#initialize' do it 'creates a namespace object' do expect(Gitlab::Kubernetes::Namespace).to( receive(:new).with(gitlab_namespace, client, labels: gitlab_namespace_labels) ) subject end end describe '#uninstall' do before do allow(client).to receive(:create_pod).and_return(nil) allow(client).to receive(:get_config_map).and_return(nil) allow(client).to receive(:create_config_map).and_return(nil) allow(client).to receive(:delete_pod).and_return(nil) allow(namespace).to receive(:ensure_exists!).once end it 'ensures the namespace exists before creating the POD' do expect(namespace).to receive(:ensure_exists!).once.ordered expect(client).to receive(:create_pod).once.ordered subject.uninstall(command) end it 'removes an existing pod before installing' do expect(client).to receive(:delete_pod).with('install-app-name', 'gitlab-managed-apps').once.ordered expect(client).to receive(:create_pod).once.ordered subject.uninstall(command) end context 'with a ConfigMap' do let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application_name, files).generate } it 'creates a ConfigMap on kubeclient' do expect(client).to receive(:create_config_map).with(resource).once subject.install(command) end context 'config map already exists' do before do expect(client).to receive(:get_config_map).with("values-content-configuration-#{application_name}", gitlab_namespace).and_return(resource) end it 'updates the config map' do expect(client).to receive(:update_config_map).with(resource).once subject.install(command) end end end end describe '#install' do before do allow(client).to receive(:create_pod).and_return(nil) allow(client).to receive(:get_config_map).and_return(nil) allow(client).to receive(:create_config_map).and_return(nil) allow(client).to receive(:create_service_account).and_return(nil) allow(client).to receive(:delete_pod).and_return(nil) allow(namespace).to receive(:ensure_exists!).once end it 'ensures the namespace exists before creating the POD' do expect(namespace).to receive(:ensure_exists!).once.ordered expect(client).to receive(:create_pod).once.ordered subject.install(command) end it 'removes an existing pod before installing' do expect(client).to receive(:delete_pod).with('install-app-name', 'gitlab-managed-apps').once.ordered expect(client).to receive(:create_pod).once.ordered subject.install(command) end context 'with a ConfigMap' do let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application_name, files).generate } it 'creates a ConfigMap on kubeclient' do expect(client).to receive(:create_config_map).with(resource).once subject.install(command) end context 'config map already exists' do before do expect(client).to receive(:get_config_map).with("values-content-configuration-#{application_name}", gitlab_namespace).and_return(resource) end it 'updates the config map' do expect(client).to receive(:update_config_map).with(resource).once subject.install(command) end end end context 'without a service account' do it 'does not create a service account on kubeclient' do expect(client).not_to receive(:create_service_account) expect(client).not_to receive(:update_cluster_role_binding) subject.install(command) end end context 'with a service account' do let(:command) { Gitlab::Kubernetes::Helm::V2::InitCommand.new(name: application_name, files: files, rbac: rbac) } context 'rbac-enabled cluster' do let(:rbac) { true } let(:service_account_resource) do Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' }) end let(:cluster_role_binding_resource) do Kubeclient::Resource.new( metadata: { name: 'tiller-admin' }, roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' }, subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }] ) end context 'service account does not exist' do before do expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil)) end it 'creates a service account, followed the cluster role binding on kubeclient' do expect(client).to receive(:create_service_account).with(service_account_resource).once.ordered expect(client).to receive(:update_cluster_role_binding).with(cluster_role_binding_resource).once.ordered subject.install(command) end end context 'service account already exists' do before do expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_return(service_account_resource) end it 'updates the service account, followed by creating the cluster role binding' do expect(client).to receive(:update_service_account).with(service_account_resource).once.ordered expect(client).to receive(:update_cluster_role_binding).with(cluster_role_binding_resource).once.ordered subject.install(command) end end context 'a non-404 error is thrown' do before do expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) end it 'raises an error' do expect { subject.install(command) }.to raise_error(Kubeclient::HttpError) end end end context 'legacy abac cluster' do it 'does not create a service account on kubeclient' do expect(client).not_to receive(:create_service_account) expect(client).not_to receive(:update_cluster_role_binding) subject.install(command) end end end end describe '#status' do let(:phase) { Gitlab::Kubernetes::Pod::RUNNING } let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation it 'fetches POD phase from kubernetes cluster' do expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod) expect(subject.status(command.pod_name)).to eq(phase) end end describe '#log' do let(:log) { 'some output' } let(:response) { RestClient::Response.new(log) } it 'fetches POD phase from kubernetes cluster' do expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response) expect(subject.log(command.pod_name)).to eq(log) end end describe '#delete_pod!' do it 'deletes the POD from kubernetes cluster' do expect(client).to receive(:delete_pod).with('install-app-name', 'gitlab-managed-apps').once subject.delete_pod!('install-app-name') end context 'when the resource being deleted does not exist' do it 'catches the error' do expect(client).to receive(:delete_pod).with('install-app-name', 'gitlab-managed-apps') .and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil)) subject.delete_pod!('install-app-name') end end end describe '#get_config_map' do before do allow(namespace).to receive(:ensure_exists!).once allow(client).to receive(:get_config_map).and_return(nil) end it 'ensures the namespace exists before retrieving the config map' do expect(namespace).to receive(:ensure_exists!).once subject.get_config_map('example-config-map-name') end it 'gets the config map on kubeclient' do expect(client).to receive(:get_config_map) .with('example-config-map-name', namespace.name) .once subject.get_config_map('example-config-map-name') end end end