Merge branch '29398-support-kubernetes-rbac-for-gitlab-managed-apps' into 'master'
Enable Kubernetes RBAC for GitLab Managed Apps for existing clusters See merge request gitlab-org/gitlab-ce!21127
This commit is contained in:
commit
4ad792dede
|
@ -157,7 +157,8 @@ class Projects::ClustersController < Projects::ApplicationController
|
|||
:namespace,
|
||||
:api_url,
|
||||
:token,
|
||||
:ca_cert
|
||||
:ca_cert,
|
||||
:authorization_type
|
||||
]).merge(
|
||||
provider_type: :user,
|
||||
platform_type: :kubernetes
|
||||
|
|
|
@ -11,4 +11,8 @@ module ClustersHelper
|
|||
render 'projects/clusters/gcp_signup_offer_banner'
|
||||
end
|
||||
end
|
||||
|
||||
def rbac_clusters_feature_enabled?
|
||||
Feature.enabled?(:rbac_clusters)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,8 @@ module Clusters
|
|||
def install_command
|
||||
Gitlab::Kubernetes::Helm::InitCommand.new(
|
||||
name: name,
|
||||
files: files
|
||||
files: files,
|
||||
rbac: cluster.platform_kubernetes_rbac?
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ module Clusters
|
|||
Gitlab::Kubernetes::Helm::InstallCommand.new(
|
||||
name: name,
|
||||
version: VERSION,
|
||||
rbac: cluster.platform_kubernetes_rbac?,
|
||||
chart: chart,
|
||||
files: files
|
||||
)
|
||||
|
|
|
@ -40,6 +40,7 @@ module Clusters
|
|||
Gitlab::Kubernetes::Helm::InstallCommand.new(
|
||||
name: name,
|
||||
version: VERSION,
|
||||
rbac: cluster.platform_kubernetes_rbac?,
|
||||
chart: chart,
|
||||
files: files,
|
||||
repository: repository
|
||||
|
|
|
@ -48,6 +48,7 @@ module Clusters
|
|||
Gitlab::Kubernetes::Helm::InstallCommand.new(
|
||||
name: name,
|
||||
version: VERSION,
|
||||
rbac: cluster.platform_kubernetes_rbac?,
|
||||
chart: chart,
|
||||
files: files
|
||||
)
|
||||
|
@ -71,7 +72,7 @@ module Clusters
|
|||
private
|
||||
|
||||
def kube_client
|
||||
cluster&.kubeclient
|
||||
cluster&.kubeclient&.core_client
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,6 +33,7 @@ module Clusters
|
|||
Gitlab::Kubernetes::Helm::InstallCommand.new(
|
||||
name: name,
|
||||
version: VERSION,
|
||||
rbac: cluster.platform_kubernetes_rbac?,
|
||||
chart: chart,
|
||||
files: files,
|
||||
repository: repository
|
||||
|
|
|
@ -42,6 +42,7 @@ module Clusters
|
|||
delegate :on_creation?, to: :provider, allow_nil: true
|
||||
|
||||
delegate :active?, to: :platform_kubernetes, prefix: true, allow_nil: true
|
||||
delegate :rbac?, to: :platform_kubernetes, prefix: true, allow_nil: true
|
||||
delegate :installed?, to: :application_helm, prefix: true, allow_nil: true
|
||||
delegate :installed?, to: :application_ingress, prefix: true, allow_nil: true
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ module Clusters
|
|||
class Kubernetes < ActiveRecord::Base
|
||||
include Gitlab::Kubernetes
|
||||
include ReactiveCaching
|
||||
include EnumWithNil
|
||||
|
||||
self.table_name = 'cluster_platforms_kubernetes'
|
||||
self.reactive_cache_key = ->(kubernetes) { [kubernetes.class.model_name.singular, kubernetes.id] }
|
||||
|
@ -47,6 +48,12 @@ module Clusters
|
|||
|
||||
alias_method :active?, :enabled?
|
||||
|
||||
enum_with_nil authorization_type: {
|
||||
unknown_authorization: nil,
|
||||
rbac: 1,
|
||||
abac: 2
|
||||
}
|
||||
|
||||
def actual_namespace
|
||||
if namespace.present?
|
||||
namespace
|
||||
|
@ -95,7 +102,7 @@ module Clusters
|
|||
end
|
||||
|
||||
def kubeclient
|
||||
@kubeclient ||= build_kubeclient!
|
||||
@kubeclient ||= build_kube_client!(api_groups: ['api', 'apis/rbac.authorization.k8s.io'])
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -115,15 +122,16 @@ module Clusters
|
|||
slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
|
||||
end
|
||||
|
||||
def build_kubeclient!(api_path: 'api', api_version: 'v1')
|
||||
def build_kube_client!(api_groups: ['api'], api_version: 'v1')
|
||||
raise "Incomplete settings" unless api_url && actual_namespace
|
||||
|
||||
unless (username && password) || token
|
||||
raise "Either username/password or token is required to access API"
|
||||
end
|
||||
|
||||
::Kubeclient::Client.new(
|
||||
join_api_url(api_path),
|
||||
Gitlab::Kubernetes::KubeClient.new(
|
||||
api_url,
|
||||
api_groups,
|
||||
api_version,
|
||||
auth_options: kubeclient_auth_options,
|
||||
ssl_options: kubeclient_ssl_options,
|
||||
|
@ -133,7 +141,7 @@ module Clusters
|
|||
|
||||
# Returns a hash of all pods in the namespace
|
||||
def read_pods
|
||||
kubeclient = build_kubeclient!
|
||||
kubeclient = build_kube_client!
|
||||
|
||||
kubeclient.get_pods(namespace: actual_namespace).as_json
|
||||
rescue Kubeclient::HttpError => err
|
||||
|
@ -157,15 +165,6 @@ module Clusters
|
|||
{ bearer_token: token }
|
||||
end
|
||||
|
||||
def join_api_url(api_path)
|
||||
url = URI.parse(api_url)
|
||||
prefix = url.path.sub(%r{/+\z}, '')
|
||||
|
||||
url.path = [prefix, api_path].join("/")
|
||||
|
||||
url.to_s
|
||||
end
|
||||
|
||||
def terminal_auth
|
||||
{
|
||||
token: token,
|
||||
|
|
|
@ -96,10 +96,10 @@ class KubernetesService < DeploymentService
|
|||
|
||||
# Check we can connect to the Kubernetes API
|
||||
def test(*args)
|
||||
kubeclient = build_kubeclient!
|
||||
kubeclient = build_kube_client!
|
||||
|
||||
kubeclient.discover
|
||||
{ success: kubeclient.discovered, result: "Checked API discovery endpoint" }
|
||||
kubeclient.core_client.discover
|
||||
{ success: kubeclient.core_client.discovered, result: "Checked API discovery endpoint" }
|
||||
rescue => err
|
||||
{ success: false, result: err }
|
||||
end
|
||||
|
@ -144,7 +144,7 @@ class KubernetesService < DeploymentService
|
|||
end
|
||||
|
||||
def kubeclient
|
||||
@kubeclient ||= build_kubeclient!
|
||||
@kubeclient ||= build_kube_client!(api_groups: ['api', 'apis/rbac.authorization.k8s.io'])
|
||||
end
|
||||
|
||||
def deprecated?
|
||||
|
@ -182,11 +182,12 @@ class KubernetesService < DeploymentService
|
|||
slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
|
||||
end
|
||||
|
||||
def build_kubeclient!(api_path: 'api', api_version: 'v1')
|
||||
def build_kube_client!(api_groups: ['api'], api_version: 'v1')
|
||||
raise "Incomplete settings" unless api_url && actual_namespace && token
|
||||
|
||||
::Kubeclient::Client.new(
|
||||
join_api_url(api_path),
|
||||
Gitlab::Kubernetes::KubeClient.new(
|
||||
api_url,
|
||||
api_groups,
|
||||
api_version,
|
||||
auth_options: kubeclient_auth_options,
|
||||
ssl_options: kubeclient_ssl_options,
|
||||
|
@ -196,7 +197,7 @@ class KubernetesService < DeploymentService
|
|||
|
||||
# Returns a hash of all pods in the namespace
|
||||
def read_pods
|
||||
kubeclient = build_kubeclient!
|
||||
kubeclient = build_kube_client!
|
||||
|
||||
kubeclient.get_pods(namespace: actual_namespace).as_json
|
||||
rescue Kubeclient::HttpError => err
|
||||
|
@ -220,15 +221,6 @@ class KubernetesService < DeploymentService
|
|||
{ bearer_token: token }
|
||||
end
|
||||
|
||||
def join_api_url(api_path)
|
||||
url = URI.parse(api_url)
|
||||
prefix = url.path.sub(%r{/+\z}, '')
|
||||
|
||||
url.path = [prefix, api_path].join("/")
|
||||
|
||||
url.to_s
|
||||
end
|
||||
|
||||
def terminal_auth
|
||||
{
|
||||
token: token,
|
||||
|
|
|
@ -25,5 +25,14 @@
|
|||
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
|
||||
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
|
||||
|
||||
- if rbac_clusters_feature_enabled?
|
||||
.form-group
|
||||
.form-check
|
||||
= platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input' }, 'rbac', 'abac'
|
||||
= platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
|
||||
.form-text.text-muted
|
||||
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
|
||||
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
|
||||
|
||||
.form-group
|
||||
= field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
|
||||
|
|
|
@ -26,5 +26,14 @@
|
|||
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
|
||||
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
|
||||
|
||||
- if rbac_clusters_feature_enabled?
|
||||
.form-group
|
||||
.form-check
|
||||
= platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
|
||||
= platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
|
||||
.form-text.text-muted
|
||||
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
|
||||
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
|
||||
|
||||
.form-group
|
||||
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Support Kubernetes RBAC for GitLab Managed Apps when adding a existing cluster
|
||||
merge_request: 21127
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddAuthorizationTypeToClusterPlatformsKubernetes < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :cluster_platforms_kubernetes, :authorization_type, :integer, limit: 2
|
||||
end
|
||||
end
|
|
@ -588,6 +588,7 @@ ActiveRecord::Schema.define(version: 20180826111825) do
|
|||
t.string "encrypted_password_iv"
|
||||
t.text "encrypted_token"
|
||||
t.string "encrypted_token_iv"
|
||||
t.integer "authorization_type", limit: 2
|
||||
end
|
||||
|
||||
add_index "cluster_platforms_kubernetes", ["cluster_id"], name: "index_cluster_platforms_kubernetes_on_cluster_id", unique: true, using: :btree
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Kubernetes
|
||||
class ClusterRoleBinding
|
||||
attr_reader :name, :cluster_role_name, :subjects
|
||||
|
||||
def initialize(name, cluster_role_name, subjects)
|
||||
@name = name
|
||||
@cluster_role_name = cluster_role_name
|
||||
@subjects = subjects
|
||||
end
|
||||
|
||||
def generate
|
||||
::Kubeclient::Resource.new.tap do |resource|
|
||||
resource.metadata = metadata
|
||||
resource.roleRef = role_ref
|
||||
resource.subjects = subjects
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metadata
|
||||
{ name: name }
|
||||
end
|
||||
|
||||
def role_ref
|
||||
{
|
||||
apiGroup: 'rbac.authorization.k8s.io',
|
||||
kind: 'ClusterRole',
|
||||
name: cluster_role_name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,9 @@ module Gitlab
|
|||
module Helm
|
||||
HELM_VERSION = '2.7.2'.freeze
|
||||
NAMESPACE = 'gitlab-managed-apps'.freeze
|
||||
SERVICE_ACCOUNT = 'tiller'.freeze
|
||||
CLUSTER_ROLE_BINDING = 'tiller-admin'.freeze
|
||||
CLUSTER_ROLE = 'cluster-admin'.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,11 @@ module Gitlab
|
|||
|
||||
def install(command)
|
||||
namespace.ensure_exists!
|
||||
|
||||
create_service_account(command)
|
||||
create_cluster_role_binding(command)
|
||||
create_config_map(command)
|
||||
|
||||
kubeclient.create_pod(command.pod_resource)
|
||||
end
|
||||
|
||||
|
@ -41,6 +45,50 @@ module Gitlab
|
|||
kubeclient.create_config_map(config_map_resource)
|
||||
end
|
||||
end
|
||||
|
||||
def create_service_account(command)
|
||||
command.service_account_resource.tap do |service_account_resource|
|
||||
break unless service_account_resource
|
||||
|
||||
if service_account_exists?(service_account_resource)
|
||||
kubeclient.update_service_account(service_account_resource)
|
||||
else
|
||||
kubeclient.create_service_account(service_account_resource)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_cluster_role_binding(command)
|
||||
command.cluster_role_binding_resource.tap do |cluster_role_binding_resource|
|
||||
break unless cluster_role_binding_resource
|
||||
|
||||
if cluster_role_binding_exists?(cluster_role_binding_resource)
|
||||
kubeclient.update_cluster_role_binding(cluster_role_binding_resource)
|
||||
else
|
||||
kubeclient.create_cluster_role_binding(cluster_role_binding_resource)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def service_account_exists?(resource)
|
||||
resource_exists? do
|
||||
kubeclient.get_service_account(resource.metadata.name, resource.metadata.namespace)
|
||||
end
|
||||
end
|
||||
|
||||
def cluster_role_binding_exists?(resource)
|
||||
resource_exists? do
|
||||
kubeclient.get_cluster_role_binding(resource.metadata.name)
|
||||
end
|
||||
end
|
||||
|
||||
def resource_exists?
|
||||
yield
|
||||
rescue ::Kubeclient::HttpError => e
|
||||
raise e unless e.error_code == 404
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,9 @@ module Gitlab
|
|||
module Helm
|
||||
module BaseCommand
|
||||
def pod_resource
|
||||
Gitlab::Kubernetes::Helm::Pod.new(self, namespace).generate
|
||||
pod_service_account_name = rbac? ? service_account_name : nil
|
||||
|
||||
Gitlab::Kubernetes::Helm::Pod.new(self, namespace, service_account_name: pod_service_account_name).generate
|
||||
end
|
||||
|
||||
def generate_script
|
||||
|
@ -26,6 +28,14 @@ module Gitlab
|
|||
Gitlab::Kubernetes::ConfigMap.new(name, files).generate
|
||||
end
|
||||
|
||||
def service_account_resource
|
||||
nil
|
||||
end
|
||||
|
||||
def cluster_role_binding_resource
|
||||
nil
|
||||
end
|
||||
|
||||
def file_names
|
||||
files.keys
|
||||
end
|
||||
|
@ -34,6 +44,10 @@ module Gitlab
|
|||
raise "Not implemented"
|
||||
end
|
||||
|
||||
def rbac?
|
||||
raise "Not implemented"
|
||||
end
|
||||
|
||||
def files
|
||||
raise "Not implemented"
|
||||
end
|
||||
|
@ -47,6 +61,10 @@ module Gitlab
|
|||
def namespace
|
||||
Gitlab::Kubernetes::Helm::NAMESPACE
|
||||
end
|
||||
|
||||
def service_account_name
|
||||
Gitlab::Kubernetes::Helm::SERVICE_ACCOUNT
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,9 +6,10 @@ module Gitlab
|
|||
|
||||
attr_reader :name, :files
|
||||
|
||||
def initialize(name:, files:)
|
||||
def initialize(name:, files:, rbac:)
|
||||
@name = name
|
||||
@files = files
|
||||
@rbac = rbac
|
||||
end
|
||||
|
||||
def generate_script
|
||||
|
@ -17,15 +18,62 @@ module Gitlab
|
|||
].join("\n")
|
||||
end
|
||||
|
||||
def rbac?
|
||||
@rbac
|
||||
end
|
||||
|
||||
def service_account_resource
|
||||
return unless rbac?
|
||||
|
||||
Gitlab::Kubernetes::ServiceAccount.new(service_account_name, namespace).generate
|
||||
end
|
||||
|
||||
def cluster_role_binding_resource
|
||||
return unless rbac?
|
||||
|
||||
subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: namespace }]
|
||||
|
||||
Gitlab::Kubernetes::ClusterRoleBinding.new(
|
||||
cluster_role_binding_name,
|
||||
cluster_role_name,
|
||||
subjects
|
||||
).generate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init_helm_command
|
||||
tls_flags = "--tiller-tls" \
|
||||
" --tiller-tls-verify --tls-ca-cert #{files_dir}/ca.pem" \
|
||||
" --tiller-tls-cert #{files_dir}/cert.pem" \
|
||||
" --tiller-tls-key #{files_dir}/key.pem"
|
||||
command = %w[helm init] + init_command_flags
|
||||
|
||||
"helm init #{tls_flags} >/dev/null"
|
||||
command.shelljoin + " >/dev/null\n"
|
||||
end
|
||||
|
||||
def init_command_flags
|
||||
tls_flags + optional_service_account_flag
|
||||
end
|
||||
|
||||
def tls_flags
|
||||
[
|
||||
'--tiller-tls',
|
||||
'--tiller-tls-verify',
|
||||
'--tls-ca-cert', "#{files_dir}/ca.pem",
|
||||
'--tiller-tls-cert', "#{files_dir}/cert.pem",
|
||||
'--tiller-tls-key', "#{files_dir}/key.pem"
|
||||
]
|
||||
end
|
||||
|
||||
def optional_service_account_flag
|
||||
return [] unless rbac?
|
||||
|
||||
['--service-account', service_account_name]
|
||||
end
|
||||
|
||||
def cluster_role_binding_name
|
||||
Gitlab::Kubernetes::Helm::CLUSTER_ROLE_BINDING
|
||||
end
|
||||
|
||||
def cluster_role_name
|
||||
Gitlab::Kubernetes::Helm::CLUSTER_ROLE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,10 +6,11 @@ module Gitlab
|
|||
|
||||
attr_reader :name, :files, :chart, :version, :repository
|
||||
|
||||
def initialize(name:, chart:, files:, version: nil, repository: nil)
|
||||
def initialize(name:, chart:, files:, rbac:, version: nil, repository: nil)
|
||||
@name = name
|
||||
@chart = chart
|
||||
@version = version
|
||||
@rbac = rbac
|
||||
@files = files
|
||||
@repository = repository
|
||||
end
|
||||
|
@ -22,6 +23,10 @@ module Gitlab
|
|||
].compact.join("\n")
|
||||
end
|
||||
|
||||
def rbac?
|
||||
@rbac
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init_command
|
||||
|
@ -29,28 +34,51 @@ module Gitlab
|
|||
end
|
||||
|
||||
def repository_command
|
||||
"helm repo add #{name} #{repository}" if repository
|
||||
['helm', 'repo', 'add', name, repository].shelljoin if repository
|
||||
end
|
||||
|
||||
def script_command
|
||||
init_flags = "--name #{name}#{optional_tls_flags}#{optional_version_flag}" \
|
||||
" --namespace #{Gitlab::Kubernetes::Helm::NAMESPACE}" \
|
||||
" -f /data/helm/#{name}/config/values.yaml"
|
||||
command = ['helm', 'install', chart] + install_command_flags
|
||||
|
||||
"helm install #{chart} #{init_flags} >/dev/null\n"
|
||||
command.shelljoin + " >/dev/null\n"
|
||||
end
|
||||
|
||||
def install_command_flags
|
||||
name_flag = ['--name', name]
|
||||
namespace_flag = ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE]
|
||||
value_flag = ['-f', "/data/helm/#{name}/config/values.yaml"]
|
||||
|
||||
name_flag +
|
||||
optional_tls_flags +
|
||||
optional_version_flag +
|
||||
optional_rbac_create_flag +
|
||||
namespace_flag +
|
||||
value_flag
|
||||
end
|
||||
|
||||
def optional_rbac_create_flag
|
||||
return [] unless rbac?
|
||||
|
||||
# jupyterhub helm chart is using rbac.enabled
|
||||
# https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tree/master/jupyterhub
|
||||
%w[--set rbac.create=true,rbac.enabled=true]
|
||||
end
|
||||
|
||||
def optional_version_flag
|
||||
" --version #{version}" if version
|
||||
return [] unless version
|
||||
|
||||
['--version', version]
|
||||
end
|
||||
|
||||
def optional_tls_flags
|
||||
return unless files.key?(:'ca.pem')
|
||||
return [] unless files.key?(:'ca.pem')
|
||||
|
||||
" --tls" \
|
||||
" --tls-ca-cert #{files_dir}/ca.pem" \
|
||||
" --tls-cert #{files_dir}/cert.pem" \
|
||||
" --tls-key #{files_dir}/key.pem"
|
||||
[
|
||||
'--tls',
|
||||
'--tls-ca-cert', "#{files_dir}/ca.pem",
|
||||
'--tls-cert', "#{files_dir}/cert.pem",
|
||||
'--tls-key', "#{files_dir}/key.pem"
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,10 @@ module Gitlab
|
|||
module Kubernetes
|
||||
module Helm
|
||||
class Pod
|
||||
def initialize(command, namespace_name)
|
||||
def initialize(command, namespace_name, service_account_name: nil)
|
||||
@command = command
|
||||
@namespace_name = namespace_name
|
||||
@service_account_name = service_account_name
|
||||
end
|
||||
|
||||
def generate
|
||||
|
@ -12,13 +13,14 @@ module Gitlab
|
|||
|
||||
spec[:volumes] = volumes_specification
|
||||
spec[:containers][0][:volumeMounts] = volume_mounts_specification
|
||||
spec[:serviceAccountName] = service_account_name if service_account_name
|
||||
|
||||
::Kubeclient::Resource.new(metadata: metadata, spec: spec)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :command, :namespace_name, :kubeclient, :config_map
|
||||
attr_reader :command, :namespace_name, :service_account_name
|
||||
|
||||
def container_specification
|
||||
{
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
module Gitlab
|
||||
module Kubernetes
|
||||
# Wrapper around Kubeclient::Client to dispatch
|
||||
# the right message to the client that can respond to the message.
|
||||
# We must have a kubeclient for each ApiGroup as there is no
|
||||
# other way to use the Kubeclient gem.
|
||||
#
|
||||
# See https://github.com/abonas/kubeclient/issues/348.
|
||||
class KubeClient
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
SUPPORTED_API_GROUPS = [
|
||||
'api',
|
||||
'apis/rbac.authorization.k8s.io',
|
||||
'apis/extensions'
|
||||
].freeze
|
||||
|
||||
# Core API methods delegates to the core api group client
|
||||
delegate :get_pods,
|
||||
:get_secrets,
|
||||
:get_config_map,
|
||||
:get_namespace,
|
||||
:get_pod,
|
||||
:get_service,
|
||||
:get_service_account,
|
||||
:delete_pod,
|
||||
:create_config_map,
|
||||
:create_namespace,
|
||||
:create_pod,
|
||||
:create_service_account,
|
||||
:update_config_map,
|
||||
:update_service_account,
|
||||
to: :core_client
|
||||
|
||||
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
|
||||
# group client
|
||||
delegate :create_cluster_role_binding,
|
||||
:get_cluster_role_binding,
|
||||
:update_cluster_role_binding,
|
||||
to: :rbac_client
|
||||
|
||||
# Deployments resource is currently on the apis/extensions api group
|
||||
delegate :get_deployments,
|
||||
to: :extensions_client
|
||||
|
||||
# non-entity methods that can only work with the core client
|
||||
# as it uses the pods/log resource
|
||||
delegate :get_pod_log,
|
||||
:watch_pod_log,
|
||||
to: :core_client
|
||||
|
||||
def initialize(api_prefix, api_groups = ['api'], api_version = 'v1', **kubeclient_options)
|
||||
raise ArgumentError unless check_api_groups_supported?(api_groups)
|
||||
|
||||
@api_prefix = api_prefix
|
||||
@api_groups = api_groups
|
||||
@api_version = api_version
|
||||
@kubeclient_options = kubeclient_options
|
||||
end
|
||||
|
||||
def discover!
|
||||
clients.each(&:discover)
|
||||
end
|
||||
|
||||
def clients
|
||||
hashed_clients.values
|
||||
end
|
||||
|
||||
def core_client
|
||||
hashed_clients['api']
|
||||
end
|
||||
|
||||
def rbac_client
|
||||
hashed_clients['apis/rbac.authorization.k8s.io']
|
||||
end
|
||||
|
||||
def extensions_client
|
||||
hashed_clients['apis/extensions']
|
||||
end
|
||||
|
||||
def hashed_clients
|
||||
strong_memoize(:hashed_clients) do
|
||||
@api_groups.map do |api_group|
|
||||
api_url = join_api_url(@api_prefix, api_group)
|
||||
[api_group, ::Kubeclient::Client.new(api_url, @api_version, **@kubeclient_options)]
|
||||
end.to_h
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_api_groups_supported?(api_groups)
|
||||
api_groups.all? {|api_group| SUPPORTED_API_GROUPS.include?(api_group) }
|
||||
end
|
||||
|
||||
def join_api_url(api_prefix, api_path)
|
||||
url = URI.parse(api_prefix)
|
||||
prefix = url.path.sub(%r{/+\z}, '')
|
||||
|
||||
url.path = [prefix, api_path].join("/")
|
||||
|
||||
url.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Kubernetes
|
||||
class ServiceAccount
|
||||
attr_reader :name, :namespace_name
|
||||
|
||||
def initialize(name, namespace_name)
|
||||
@name = name
|
||||
@namespace_name = namespace_name
|
||||
end
|
||||
|
||||
def generate
|
||||
::Kubeclient::Resource.new(metadata: metadata)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metadata
|
||||
{
|
||||
name: name,
|
||||
namespace: namespace_name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1359,6 +1359,9 @@ msgstr ""
|
|||
msgid "ClusterIntegration|Did you know?"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1542,6 +1545,9 @@ msgstr ""
|
|||
msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1620,6 +1626,9 @@ msgstr ""
|
|||
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -274,13 +274,45 @@ describe Projects::ClustersController do
|
|||
context 'when creates a cluster' do
|
||||
it 'creates a new cluster' do
|
||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||
|
||||
expect { go }.to change { Clusters::Cluster.count }
|
||||
.and change { Clusters::Platforms::Kubernetes.count }
|
||||
|
||||
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
|
||||
|
||||
expect(project.clusters.first).to be_user
|
||||
expect(project.clusters.first).to be_kubernetes
|
||||
end
|
||||
end
|
||||
|
||||
context 'when creates a RBAC-enabled cluster' do
|
||||
let(:params) do
|
||||
{
|
||||
cluster: {
|
||||
name: 'new-cluster',
|
||||
platform_kubernetes_attributes: {
|
||||
api_url: 'http://my-url',
|
||||
token: 'test',
|
||||
namespace: 'aaa',
|
||||
authorization_type: 'rbac'
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a new cluster' do
|
||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||
|
||||
expect { go }.to change { Clusters::Cluster.count }
|
||||
.and change { Clusters::Platforms::Kubernetes.count }
|
||||
|
||||
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
|
||||
|
||||
expect(project.clusters.first).to be_user
|
||||
expect(project.clusters.first).to be_kubernetes
|
||||
expect(project.clusters.first).to be_platform_kubernetes_rbac
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
|
|
|
@ -16,5 +16,9 @@ FactoryBot.define do
|
|||
platform_kubernetes.ca_cert = File.read(pem_file)
|
||||
end
|
||||
end
|
||||
|
||||
trait :rbac_enabled do
|
||||
authorization_type :rbac
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,6 +38,28 @@ describe 'User Cluster', :js do
|
|||
end
|
||||
end
|
||||
|
||||
context 'rbac_clusters feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(rbac_clusters: true)
|
||||
|
||||
fill_in 'cluster_name', with: 'dev-cluster'
|
||||
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com'
|
||||
fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token'
|
||||
check 'cluster_platform_kubernetes_attributes_authorization_type'
|
||||
click_button 'Add Kubernetes cluster'
|
||||
end
|
||||
|
||||
it 'user sees a cluster details page' do
|
||||
expect(page).to have_content('Kubernetes cluster integration')
|
||||
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
|
||||
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
|
||||
.to have_content('http://example.com')
|
||||
expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
|
||||
.to have_content('my-token')
|
||||
expect(page.find_field('cluster[platform_kubernetes_attributes][authorization_type]', disabled: true)).to be_checked
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user filled form with invalid parameters' do
|
||||
before do
|
||||
click_button 'Add Kubernetes cluster'
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Kubernetes::ClusterRoleBinding do
|
||||
let(:cluster_role_binding) { described_class.new(name, cluster_role_name, subjects) }
|
||||
let(:name) { 'cluster-role-binding-name' }
|
||||
let(:cluster_role_name) { 'cluster-admin' }
|
||||
|
||||
let(:subjects) { [{ kind: 'ServiceAccount', name: 'sa', namespace: 'ns' }] }
|
||||
|
||||
describe '#generate' do
|
||||
let(:role_ref) do
|
||||
{
|
||||
apiGroup: 'rbac.authorization.k8s.io',
|
||||
kind: 'ClusterRole',
|
||||
name: cluster_role_name
|
||||
}
|
||||
end
|
||||
|
||||
let(:resource) do
|
||||
::Kubeclient::Resource.new(
|
||||
metadata: { name: name },
|
||||
roleRef: role_ref,
|
||||
subjects: subjects
|
||||
)
|
||||
end
|
||||
|
||||
subject { cluster_role_binding.generate }
|
||||
|
||||
it 'should build a Kubeclient Resource' do
|
||||
is_expected.to eq(resource)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,9 +5,18 @@ describe Gitlab::Kubernetes::Helm::Api do
|
|||
let(:helm) { described_class.new(client) }
|
||||
let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
|
||||
let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) }
|
||||
let(:application) { create(:clusters_applications_prometheus) }
|
||||
let(:application_name) { 'app-name' }
|
||||
let(:rbac) { false }
|
||||
let(:files) { {} }
|
||||
|
||||
let(:command) { application.install_command }
|
||||
let(:command) do
|
||||
Gitlab::Kubernetes::Helm::InstallCommand.new(
|
||||
name: application_name,
|
||||
chart: 'chart-name',
|
||||
rbac: rbac,
|
||||
files: files
|
||||
)
|
||||
end
|
||||
|
||||
subject { helm }
|
||||
|
||||
|
@ -28,6 +37,8 @@ describe Gitlab::Kubernetes::Helm::Api do
|
|||
before do
|
||||
allow(client).to receive(:create_pod).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(:create_cluster_role_binding).and_return(nil)
|
||||
allow(namespace).to receive(:ensure_exists!).once
|
||||
end
|
||||
|
||||
|
@ -39,7 +50,7 @@ describe Gitlab::Kubernetes::Helm::Api do
|
|||
end
|
||||
|
||||
context 'with a ConfigMap' do
|
||||
let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application.name, application.files).generate }
|
||||
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
|
||||
|
@ -47,6 +58,96 @@ describe Gitlab::Kubernetes::Helm::Api do
|
|||
subject.install(command)
|
||||
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(:create_cluster_role_binding)
|
||||
|
||||
subject.install(command)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a service account' do
|
||||
let(:command) { Gitlab::Kubernetes::Helm::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 and cluster role binding does not exist' do
|
||||
before do
|
||||
expect(client).to receive('get_service_account').with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::HttpError.new(404, 'Not found', nil))
|
||||
expect(client).to receive('get_cluster_role_binding').with('tiller-admin').and_raise(Kubeclient::HttpError.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(:create_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)
|
||||
expect(client).to receive('get_cluster_role_binding').with('tiller-admin').and_raise(Kubeclient::HttpError.new(404, 'Not found', nil))
|
||||
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(:create_cluster_role_binding).with(cluster_role_binding_resource).once.ordered
|
||||
|
||||
subject.install(command)
|
||||
end
|
||||
end
|
||||
|
||||
context 'service account and cluster role binding already exists' do
|
||||
before do
|
||||
expect(client).to receive('get_service_account').with('tiller', 'gitlab-managed-apps').and_return(service_account_resource)
|
||||
expect(client).to receive('get_cluster_role_binding').with('tiller-admin').and_return(cluster_role_binding_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(:create_cluster_role_binding)
|
||||
|
||||
subject.install(command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#status' do
|
||||
|
|
|
@ -2,14 +2,24 @@ require 'spec_helper'
|
|||
|
||||
describe Gitlab::Kubernetes::Helm::BaseCommand do
|
||||
let(:application) { create(:clusters_applications_helm) }
|
||||
let(:rbac) { false }
|
||||
|
||||
let(:test_class) do
|
||||
Class.new do
|
||||
include Gitlab::Kubernetes::Helm::BaseCommand
|
||||
|
||||
def initialize(rbac)
|
||||
@rbac = rbac
|
||||
end
|
||||
|
||||
def name
|
||||
"test-class-name"
|
||||
end
|
||||
|
||||
def rbac?
|
||||
@rbac
|
||||
end
|
||||
|
||||
def files
|
||||
{
|
||||
some: 'value'
|
||||
|
@ -19,7 +29,7 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
|
|||
end
|
||||
|
||||
let(:base_command) do
|
||||
test_class.new
|
||||
test_class.new(rbac)
|
||||
end
|
||||
|
||||
subject { base_command }
|
||||
|
@ -34,6 +44,14 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
|
|||
it 'should returns a kubeclient resoure with pod content for application' do
|
||||
is_expected.to be_an_instance_of ::Kubeclient::Resource
|
||||
end
|
||||
|
||||
context 'when rbac is true' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it 'also returns a kubeclient resource' do
|
||||
is_expected.to be_an_instance_of ::Kubeclient::Resource
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pod_name' do
|
||||
|
|
|
@ -2,9 +2,135 @@ require 'spec_helper'
|
|||
|
||||
describe Gitlab::Kubernetes::Helm::InitCommand do
|
||||
let(:application) { create(:clusters_applications_helm) }
|
||||
let(:commands) { 'helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem >/dev/null' }
|
||||
let(:rbac) { false }
|
||||
let(:files) { {} }
|
||||
let(:init_command) { described_class.new(name: application.name, files: files, rbac: rbac) }
|
||||
|
||||
subject { described_class.new(name: application.name, files: {}) }
|
||||
let(:commands) do
|
||||
<<~EOS
|
||||
helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem >/dev/null
|
||||
EOS
|
||||
end
|
||||
|
||||
subject { init_command }
|
||||
|
||||
it_behaves_like 'helm commands'
|
||||
|
||||
context 'on a rbac-enabled cluster' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it_behaves_like 'helm commands' do
|
||||
let(:commands) do
|
||||
<<~EOS
|
||||
helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem --service-account tiller >/dev/null
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#rbac?' do
|
||||
subject { init_command.rbac? }
|
||||
|
||||
context 'rbac is enabled' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'rbac is not enabled' do
|
||||
let(:rbac) { false }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#config_map_resource' do
|
||||
let(:metadata) do
|
||||
{
|
||||
name: 'values-content-configuration-helm',
|
||||
namespace: 'gitlab-managed-apps',
|
||||
labels: { name: 'values-content-configuration-helm' }
|
||||
}
|
||||
end
|
||||
|
||||
let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
|
||||
|
||||
subject { init_command.config_map_resource }
|
||||
|
||||
it 'returns a KubeClient resource with config map content for the application' do
|
||||
is_expected.to eq(resource)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pod_resource' do
|
||||
subject { init_command.pod_resource }
|
||||
|
||||
context 'rbac is enabled' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it 'generates a pod that uses the tiller serviceAccountName' do
|
||||
expect(subject.spec.serviceAccountName).to eq('tiller')
|
||||
end
|
||||
end
|
||||
|
||||
context 'rbac is not enabled' do
|
||||
let(:rbac) { false }
|
||||
|
||||
it 'generates a pod that uses the default serviceAccountName' do
|
||||
expect(subject.spec.serviceAcccountName).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#service_account_resource' do
|
||||
let(:resource) do
|
||||
Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' })
|
||||
end
|
||||
|
||||
subject { init_command.service_account_resource }
|
||||
|
||||
context 'rbac is enabled' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it 'generates a Kubeclient resource for the tiller ServiceAccount' do
|
||||
is_expected.to eq(resource)
|
||||
end
|
||||
end
|
||||
|
||||
context 'rbac is not enabled' do
|
||||
let(:rbac) { false }
|
||||
|
||||
it 'generates nothing' do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#cluster_role_binding_resource' do
|
||||
let(: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
|
||||
|
||||
subject { init_command.cluster_role_binding_resource }
|
||||
|
||||
context 'rbac is enabled' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it 'generates a Kubeclient resource for the ClusterRoleBinding for tiller' do
|
||||
is_expected.to eq(resource)
|
||||
end
|
||||
end
|
||||
|
||||
context 'rbac is not enabled' do
|
||||
let(:rbac) { false }
|
||||
|
||||
it 'generates nothing' do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,14 +3,17 @@ require 'rails_helper'
|
|||
describe Gitlab::Kubernetes::Helm::InstallCommand do
|
||||
let(:files) { { 'ca.pem': 'some file content' } }
|
||||
let(:repository) { 'https://repository.example.com' }
|
||||
let(:rbac) { false }
|
||||
let(:version) { '1.2.3' }
|
||||
|
||||
let(:install_command) do
|
||||
described_class.new(
|
||||
name: 'app-name',
|
||||
chart: 'chart-name',
|
||||
rbac: rbac,
|
||||
files: files,
|
||||
version: version, repository: repository
|
||||
version: version,
|
||||
repository: repository
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -21,9 +24,52 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
|
|||
<<~EOS
|
||||
helm init --client-only >/dev/null
|
||||
helm repo add app-name https://repository.example.com
|
||||
helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
#{helm_install_comand}
|
||||
EOS
|
||||
end
|
||||
|
||||
let(:helm_install_comand) do
|
||||
<<~EOS.squish
|
||||
helm install chart-name
|
||||
--name app-name
|
||||
--tls
|
||||
--tls-ca-cert /data/helm/app-name/config/ca.pem
|
||||
--tls-cert /data/helm/app-name/config/cert.pem
|
||||
--tls-key /data/helm/app-name/config/key.pem
|
||||
--version 1.2.3
|
||||
--namespace gitlab-managed-apps
|
||||
-f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
context 'when rbac is true' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it_behaves_like 'helm commands' do
|
||||
let(:commands) do
|
||||
<<~EOS
|
||||
helm init --client-only >/dev/null
|
||||
helm repo add app-name https://repository.example.com
|
||||
#{helm_install_command}
|
||||
EOS
|
||||
end
|
||||
|
||||
let(:helm_install_command) do
|
||||
<<~EOS.squish
|
||||
helm install chart-name
|
||||
--name app-name
|
||||
--tls
|
||||
--tls-ca-cert /data/helm/app-name/config/ca.pem
|
||||
--tls-cert /data/helm/app-name/config/cert.pem
|
||||
--tls-key /data/helm/app-name/config/key.pem
|
||||
--version 1.2.3
|
||||
--set rbac.create\\=true,rbac.enabled\\=true
|
||||
--namespace gitlab-managed-apps
|
||||
-f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no repository' do
|
||||
|
@ -32,8 +78,22 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
|
|||
it_behaves_like 'helm commands' do
|
||||
let(:commands) do
|
||||
<<~EOS
|
||||
helm init --client-only >/dev/null
|
||||
helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
helm init --client-only >/dev/null
|
||||
#{helm_install_command}
|
||||
EOS
|
||||
end
|
||||
|
||||
let(:helm_install_command) do
|
||||
<<~EOS.squish
|
||||
helm install chart-name
|
||||
--name app-name
|
||||
--tls
|
||||
--tls-ca-cert /data/helm/app-name/config/ca.pem
|
||||
--tls-cert /data/helm/app-name/config/cert.pem
|
||||
--tls-key /data/helm/app-name/config/key.pem
|
||||
--version 1.2.3
|
||||
--namespace gitlab-managed-apps
|
||||
-f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
@ -45,9 +105,19 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
|
|||
it_behaves_like 'helm commands' do
|
||||
let(:commands) do
|
||||
<<~EOS
|
||||
helm init --client-only >/dev/null
|
||||
helm repo add app-name https://repository.example.com
|
||||
helm install chart-name --name app-name --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
helm init --client-only >/dev/null
|
||||
helm repo add app-name https://repository.example.com
|
||||
#{helm_install_command}
|
||||
EOS
|
||||
end
|
||||
|
||||
let(:helm_install_command) do
|
||||
<<~EOS.squish
|
||||
helm install chart-name
|
||||
--name app-name
|
||||
--version 1.2.3
|
||||
--namespace gitlab-managed-apps
|
||||
-f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
@ -59,11 +129,60 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
|
|||
it_behaves_like 'helm commands' do
|
||||
let(:commands) do
|
||||
<<~EOS
|
||||
helm init --client-only >/dev/null
|
||||
helm repo add app-name https://repository.example.com
|
||||
helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
helm init --client-only >/dev/null
|
||||
helm repo add app-name https://repository.example.com
|
||||
#{helm_install_command}
|
||||
EOS
|
||||
end
|
||||
|
||||
let(:helm_install_command) do
|
||||
<<~EOS.squish
|
||||
helm install chart-name
|
||||
--name app-name
|
||||
--tls
|
||||
--tls-ca-cert /data/helm/app-name/config/ca.pem
|
||||
--tls-cert /data/helm/app-name/config/cert.pem
|
||||
--tls-key /data/helm/app-name/config/key.pem
|
||||
--namespace gitlab-managed-apps
|
||||
-f /data/helm/app-name/config/values.yaml >/dev/null
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#rbac?' do
|
||||
subject { install_command.rbac? }
|
||||
|
||||
context 'rbac is enabled' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'rbac is not enabled' do
|
||||
let(:rbac) { false }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pod_resource' do
|
||||
subject { install_command.pod_resource }
|
||||
|
||||
context 'rbac is enabled' do
|
||||
let(:rbac) { true }
|
||||
|
||||
it 'generates a pod that uses the tiller serviceAccountName' do
|
||||
expect(subject.spec.serviceAccountName).to eq('tiller')
|
||||
end
|
||||
end
|
||||
|
||||
context 'rbac is not enabled' do
|
||||
let(:rbac) { false }
|
||||
|
||||
it 'generates a pod that uses the default serviceAccountName' do
|
||||
expect(subject.spec.serviceAcccountName).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -84,4 +203,20 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
|
|||
is_expected.to eq(resource)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#service_account_resource' do
|
||||
subject { install_command.service_account_resource }
|
||||
|
||||
it 'returns nothing' do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#cluster_role_binding_resource' do
|
||||
subject { install_command.cluster_role_binding_resource }
|
||||
|
||||
it 'returns nothing' do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,8 +5,9 @@ describe Gitlab::Kubernetes::Helm::Pod do
|
|||
let(:app) { create(:clusters_applications_prometheus) }
|
||||
let(:command) { app.install_command }
|
||||
let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
|
||||
let(:service_account_name) { nil }
|
||||
|
||||
subject { described_class.new(command, namespace) }
|
||||
subject { described_class.new(command, namespace, service_account_name: service_account_name) }
|
||||
|
||||
context 'with a command' do
|
||||
it 'should generate a Kubeclient::Resource' do
|
||||
|
@ -58,6 +59,20 @@ describe Gitlab::Kubernetes::Helm::Pod do
|
|||
expect(volume.configMap['items'].first['key']).to eq(:'values.yaml')
|
||||
expect(volume.configMap['items'].first['path']).to eq(:'values.yaml')
|
||||
end
|
||||
|
||||
it 'should have no serviceAccountName' do
|
||||
spec = subject.generate.spec
|
||||
expect(spec.serviceAccountName).to be_nil
|
||||
end
|
||||
|
||||
context 'with a service_account_name' do
|
||||
let(:service_account_name) { 'sa' }
|
||||
|
||||
it 'should use the serviceAccountName provided' do
|
||||
spec = subject.generate.spec
|
||||
expect(spec.serviceAccountName).to eq(service_account_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,247 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Kubernetes::KubeClient do
|
||||
include KubernetesHelpers
|
||||
|
||||
let(:api_url) { 'https://kubernetes.example.com/prefix' }
|
||||
let(:api_groups) { ['api', 'apis/rbac.authorization.k8s.io'] }
|
||||
let(:api_version) { 'v1' }
|
||||
let(:kubeclient_options) { { auth_options: { bearer_token: 'xyz' } } }
|
||||
|
||||
let(:client) { described_class.new(api_url, api_groups, api_version, kubeclient_options) }
|
||||
|
||||
before do
|
||||
stub_kubeclient_discover(api_url)
|
||||
end
|
||||
|
||||
describe '#hashed_clients' do
|
||||
subject { client.hashed_clients }
|
||||
|
||||
it 'has keys from api groups' do
|
||||
expect(subject.keys).to match_array api_groups
|
||||
end
|
||||
|
||||
it 'has values of Kubeclient::Client' do
|
||||
expect(subject.values).to all(be_an_instance_of Kubeclient::Client)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clients' do
|
||||
subject { client.clients }
|
||||
|
||||
it 'is not empty' do
|
||||
is_expected.to be_present
|
||||
end
|
||||
|
||||
it 'is an array of Kubeclient::Client objects' do
|
||||
is_expected.to all(be_an_instance_of Kubeclient::Client)
|
||||
end
|
||||
|
||||
it 'has each API group url' do
|
||||
expected_urls = api_groups.map { |group| "#{api_url}/#{group}" }
|
||||
|
||||
expect(subject.map(&:api_endpoint).map(&:to_s)).to match_array(expected_urls)
|
||||
end
|
||||
|
||||
it 'has the kubeclient options' do
|
||||
subject.each do |client|
|
||||
expect(client.auth_options).to eq({ bearer_token: 'xyz' })
|
||||
end
|
||||
end
|
||||
|
||||
it 'has the api_version' do
|
||||
subject.each do |client|
|
||||
expect(client.instance_variable_get(:@api_version)).to eq('v1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#core_client' do
|
||||
subject { client.core_client }
|
||||
|
||||
it 'is a Kubeclient::Client' do
|
||||
is_expected.to be_an_instance_of Kubeclient::Client
|
||||
end
|
||||
|
||||
it 'has the core API endpoint' do
|
||||
expect(subject.api_endpoint.to_s).to match(%r{\/api\Z})
|
||||
end
|
||||
end
|
||||
|
||||
describe '#rbac_client' do
|
||||
subject { client.rbac_client }
|
||||
|
||||
it 'is a Kubeclient::Client' do
|
||||
is_expected.to be_an_instance_of Kubeclient::Client
|
||||
end
|
||||
|
||||
it 'has the RBAC API group endpoint' do
|
||||
expect(subject.api_endpoint.to_s).to match(%r{\/apis\/rbac.authorization.k8s.io\Z})
|
||||
end
|
||||
end
|
||||
|
||||
describe '#extensions_client' do
|
||||
subject { client.extensions_client }
|
||||
|
||||
let(:api_groups) { ['apis/extensions'] }
|
||||
|
||||
it 'is a Kubeclient::Client' do
|
||||
is_expected.to be_an_instance_of Kubeclient::Client
|
||||
end
|
||||
|
||||
it 'has the extensions API group endpoint' do
|
||||
expect(subject.api_endpoint.to_s).to match(%r{\/apis\/extensions\Z})
|
||||
end
|
||||
end
|
||||
|
||||
describe '#discover!' do
|
||||
it 'makes a discovery request for each API group' do
|
||||
client.discover!
|
||||
|
||||
api_groups.each do |api_group|
|
||||
discovery_url = api_url + '/' + api_group + '/v1'
|
||||
expect(WebMock).to have_requested(:get, discovery_url).once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'core API' do
|
||||
let(:core_client) { client.core_client }
|
||||
|
||||
[
|
||||
:get_pods,
|
||||
:get_secrets,
|
||||
:get_config_map,
|
||||
:get_pod,
|
||||
:get_namespace,
|
||||
:get_service,
|
||||
:get_service_account,
|
||||
:delete_pod,
|
||||
:create_config_map,
|
||||
:create_namespace,
|
||||
:create_pod,
|
||||
:create_service_account,
|
||||
:update_config_map,
|
||||
:update_service_account
|
||||
].each do |method|
|
||||
describe "##{method}" do
|
||||
it 'delegates to the core client' do
|
||||
expect(client).to delegate_method(method).to(:core_client)
|
||||
end
|
||||
|
||||
it 'responds to the method' do
|
||||
expect(client).to respond_to method
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'rbac API group' do
|
||||
let(:rbac_client) { client.rbac_client }
|
||||
|
||||
[
|
||||
:create_cluster_role_binding,
|
||||
:get_cluster_role_binding,
|
||||
:update_cluster_role_binding
|
||||
].each do |method|
|
||||
describe "##{method}" do
|
||||
it 'delegates to the rbac client' do
|
||||
expect(client).to delegate_method(method).to(:rbac_client)
|
||||
end
|
||||
|
||||
it 'responds to the method' do
|
||||
expect(client).to respond_to method
|
||||
end
|
||||
|
||||
context 'no rbac client' do
|
||||
let(:api_groups) { ['api'] }
|
||||
|
||||
it 'throws an error' do
|
||||
expect { client.public_send(method) }.to raise_error(Module::DelegationError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'extensions API group' do
|
||||
let(:api_groups) { ['apis/extensions'] }
|
||||
let(:api_version) { 'v1beta1' }
|
||||
let(:extensions_client) { client.extensions_client }
|
||||
|
||||
describe '#get_deployments' do
|
||||
it 'delegates to the extensions client' do
|
||||
expect(client).to delegate_method(:get_deployments).to(:extensions_client)
|
||||
end
|
||||
|
||||
it 'responds to the method' do
|
||||
expect(client).to respond_to :get_deployments
|
||||
end
|
||||
|
||||
context 'no extensions client' do
|
||||
let(:api_groups) { ['api'] }
|
||||
let(:api_version) { 'v1' }
|
||||
|
||||
it 'throws an error' do
|
||||
expect { client.get_deployments }.to raise_error(Module::DelegationError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'non-entity methods' do
|
||||
it 'does not proxy for non-entity methods' do
|
||||
expect(client.clients.first).to respond_to :proxy_url
|
||||
|
||||
expect(client).not_to respond_to :proxy_url
|
||||
end
|
||||
|
||||
it 'throws an error' do
|
||||
expect { client.proxy_url }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_pod_log' do
|
||||
let(:core_client) { client.core_client }
|
||||
|
||||
it 'is delegated to the core client' do
|
||||
expect(client).to delegate_method(:get_pod_log).to(:core_client)
|
||||
end
|
||||
|
||||
context 'when no core client' do
|
||||
let(:api_groups) { ['apis/extensions'] }
|
||||
|
||||
it 'throws an error' do
|
||||
expect { client.get_pod_log('pod-name') }.to raise_error(Module::DelegationError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#watch_pod_log' do
|
||||
let(:core_client) { client.core_client }
|
||||
|
||||
it 'is delegated to the core client' do
|
||||
expect(client).to delegate_method(:watch_pod_log).to(:core_client)
|
||||
end
|
||||
|
||||
context 'when no core client' do
|
||||
let(:api_groups) { ['apis/extensions'] }
|
||||
|
||||
it 'throws an error' do
|
||||
expect { client.watch_pod_log('pod-name') }.to raise_error(Module::DelegationError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'methods that do not exist on any client' do
|
||||
it 'throws an error' do
|
||||
expect { client.non_existent_method }.to raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it 'returns false for respond_to' do
|
||||
expect(client.respond_to?(:non_existent_method)).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Kubernetes::ServiceAccount do
|
||||
let(:name) { 'a_service_account' }
|
||||
let(:namespace_name) { 'a_namespace' }
|
||||
let(:service_account) { described_class.new(name, namespace_name) }
|
||||
|
||||
it { expect(service_account.name).to eq(name) }
|
||||
it { expect(service_account.namespace_name).to eq(namespace_name) }
|
||||
|
||||
describe '#generate' do
|
||||
let(:resource) do
|
||||
::Kubeclient::Resource.new(metadata: { name: name, namespace: namespace_name })
|
||||
end
|
||||
|
||||
subject { service_account.generate }
|
||||
|
||||
it 'should build a Kubeclient Resource' do
|
||||
is_expected.to eq(resource)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -47,5 +47,19 @@ describe Clusters::Applications::Helm do
|
|||
cert = OpenSSL::X509::Certificate.new(subject.files[:'cert.pem'])
|
||||
expect(cert.not_after).to be > 999.years.from_now
|
||||
end
|
||||
|
||||
describe 'rbac' do
|
||||
context 'non rbac cluster' do
|
||||
it { expect(subject).not_to be_rbac }
|
||||
end
|
||||
|
||||
context 'rbac cluster' do
|
||||
before do
|
||||
helm.cluster.platform_kubernetes.rbac!
|
||||
end
|
||||
|
||||
it { expect(subject).to be_rbac }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,9 +88,18 @@ describe Clusters::Applications::Ingress do
|
|||
expect(subject.name).to eq('ingress')
|
||||
expect(subject.chart).to eq('stable/nginx-ingress')
|
||||
expect(subject.version).to eq('0.23.0')
|
||||
expect(subject).not_to be_rbac
|
||||
expect(subject.files).to eq(ingress.files)
|
||||
end
|
||||
|
||||
context 'on a rbac enabled cluster' do
|
||||
before do
|
||||
ingress.cluster.platform_kubernetes.rbac!
|
||||
end
|
||||
|
||||
it { is_expected.to be_rbac }
|
||||
end
|
||||
|
||||
context 'application failed to install previously' do
|
||||
let(:ingress) { create(:clusters_applications_ingress, :errored, version: 'nginx') }
|
||||
|
||||
|
|
|
@ -51,10 +51,19 @@ describe Clusters::Applications::Jupyter do
|
|||
expect(subject.name).to eq('jupyter')
|
||||
expect(subject.chart).to eq('jupyter/jupyterhub')
|
||||
expect(subject.version).to eq('v0.6')
|
||||
expect(subject).not_to be_rbac
|
||||
expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/')
|
||||
expect(subject.files).to eq(jupyter.files)
|
||||
end
|
||||
|
||||
context 'on a rbac enabled cluster' do
|
||||
before do
|
||||
jupyter.cluster.platform_kubernetes.rbac!
|
||||
end
|
||||
|
||||
it { is_expected.to be_rbac }
|
||||
end
|
||||
|
||||
context 'application failed to install previously' do
|
||||
let(:jupyter) { create(:clusters_applications_jupyter, :errored, version: '0.0.1') }
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe Clusters::Applications::Prometheus do
|
||||
include KubernetesHelpers
|
||||
|
||||
include_examples 'cluster application core specs', :clusters_applications_prometheus
|
||||
include_examples 'cluster application status specs', :cluster_application_prometheus
|
||||
|
||||
|
@ -107,26 +109,14 @@ describe Clusters::Applications::Prometheus do
|
|||
end
|
||||
|
||||
context 'cluster has kubeclient' do
|
||||
let(:kubernetes_url) { 'http://example.com' }
|
||||
let(:k8s_discover_response) do
|
||||
{
|
||||
resources: [
|
||||
{
|
||||
name: 'service',
|
||||
kind: 'Service'
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
let(:kubernetes_url) { subject.cluster.platform_kubernetes.api_url }
|
||||
let(:kube_client) { subject.cluster.kubeclient.core_client }
|
||||
|
||||
let(:kube_client) { Kubeclient::Client.new(kubernetes_url) }
|
||||
|
||||
let(:cluster) { create(:cluster) }
|
||||
subject { create(:clusters_applications_prometheus, cluster: cluster) }
|
||||
subject { create(:clusters_applications_prometheus) }
|
||||
|
||||
before do
|
||||
allow(kube_client.rest_client).to receive(:get).and_return(k8s_discover_response.to_json)
|
||||
allow(subject.cluster).to receive(:kubeclient).and_return(kube_client)
|
||||
subject.cluster.platform_kubernetes.namespace = 'a-namespace'
|
||||
stub_kubeclient_discover(subject.cluster.platform_kubernetes.api_url)
|
||||
end
|
||||
|
||||
it 'creates proxy prometheus rest client' do
|
||||
|
@ -134,7 +124,7 @@ describe Clusters::Applications::Prometheus do
|
|||
end
|
||||
|
||||
it 'creates proper url' do
|
||||
expect(subject.prometheus_client.url).to eq('http://example.com/api/v1/namespaces/gitlab-managed-apps/service/prometheus-prometheus-server:80/proxy')
|
||||
expect(subject.prometheus_client.url).to eq("#{kubernetes_url}/api/v1/namespaces/gitlab-managed-apps/services/prometheus-prometheus-server:80/proxy")
|
||||
end
|
||||
|
||||
it 'copies options and headers from kube client to proxy client' do
|
||||
|
@ -164,9 +154,18 @@ describe Clusters::Applications::Prometheus do
|
|||
expect(subject.name).to eq('prometheus')
|
||||
expect(subject.chart).to eq('stable/prometheus')
|
||||
expect(subject.version).to eq('6.7.3')
|
||||
expect(subject).not_to be_rbac
|
||||
expect(subject.files).to eq(prometheus.files)
|
||||
end
|
||||
|
||||
context 'on a rbac enabled cluster' do
|
||||
before do
|
||||
prometheus.cluster.platform_kubernetes.rbac!
|
||||
end
|
||||
|
||||
it { is_expected.to be_rbac }
|
||||
end
|
||||
|
||||
context 'application failed to install previously' do
|
||||
let(:prometheus) { create(:clusters_applications_prometheus, :errored, version: '2.0.0') }
|
||||
|
||||
|
|
|
@ -46,10 +46,19 @@ describe Clusters::Applications::Runner do
|
|||
expect(subject.name).to eq('runner')
|
||||
expect(subject.chart).to eq('runner/gitlab-runner')
|
||||
expect(subject.version).to eq('0.1.31')
|
||||
expect(subject).not_to be_rbac
|
||||
expect(subject.repository).to eq('https://charts.gitlab.io')
|
||||
expect(subject.files).to eq(gitlab_runner.files)
|
||||
end
|
||||
|
||||
context 'on a rbac enabled cluster' do
|
||||
before do
|
||||
gitlab_runner.cluster.platform_kubernetes.rbac!
|
||||
end
|
||||
|
||||
it { is_expected.to be_rbac }
|
||||
end
|
||||
|
||||
context 'application failed to install previously' do
|
||||
let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') }
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ describe Clusters::Cluster do
|
|||
it { is_expected.to delegate_method(:status_reason).to(:provider) }
|
||||
it { is_expected.to delegate_method(:status_name).to(:provider) }
|
||||
it { is_expected.to delegate_method(:on_creation?).to(:provider) }
|
||||
it { is_expected.to delegate_method(:active?).to(:platform_kubernetes).with_prefix }
|
||||
it { is_expected.to delegate_method(:rbac?).to(:platform_kubernetes).with_prefix }
|
||||
it { is_expected.to delegate_method(:installed?).to(:application_helm).with_prefix }
|
||||
it { is_expected.to delegate_method(:installed?).to(:application_ingress).with_prefix }
|
||||
it { is_expected.to respond_to :project }
|
||||
|
||||
describe '.enabled' do
|
||||
|
|
|
@ -92,6 +92,30 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
|
|||
end
|
||||
end
|
||||
|
||||
describe '#kubeclient' do
|
||||
subject { kubernetes.kubeclient }
|
||||
|
||||
let(:kubernetes) { build(:cluster_platform_kubernetes, :configured, namespace: 'a-namespace') }
|
||||
|
||||
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::KubeClient) }
|
||||
end
|
||||
|
||||
describe '#rbac?' do
|
||||
subject { kubernetes.rbac? }
|
||||
|
||||
let(:kubernetes) { build(:cluster_platform_kubernetes, :configured) }
|
||||
|
||||
context 'when authorization type is rbac' do
|
||||
let(:kubernetes) { build(:cluster_platform_kubernetes, :rbac_enabled, :configured) }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when authorization type is nil' do
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#actual_namespace' do
|
||||
subject { kubernetes.actual_namespace }
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ module KubernetesHelpers
|
|||
def stub_kubeclient_discover(api_url)
|
||||
WebMock.stub_request(:get, api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body))
|
||||
WebMock.stub_request(:get, api_url + '/apis/extensions/v1beta1').to_return(kube_response(kube_v1beta1_discovery_body))
|
||||
WebMock.stub_request(:get, api_url + '/apis/rbac.authorization.k8s.io/v1').to_return(kube_response(kube_v1_rbac_authorization_discovery_body))
|
||||
end
|
||||
|
||||
def stub_kubeclient_pods(response = nil)
|
||||
|
@ -66,7 +67,8 @@ module KubernetesHelpers
|
|||
"resources" => [
|
||||
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
|
||||
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
|
||||
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" }
|
||||
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
|
||||
{ "name" => "services", "namespaced" => true, "kind" => "Service" }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
@ -77,7 +79,20 @@ module KubernetesHelpers
|
|||
"resources" => [
|
||||
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
|
||||
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
|
||||
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" }
|
||||
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
|
||||
{ "name" => "services", "namespaced" => true, "kind" => "Service" }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def kube_v1_rbac_authorization_discovery_body
|
||||
{
|
||||
"kind" => "APIResourceList",
|
||||
"resources" => [
|
||||
{ "name" => "clusterrolebindings", "namespaced" => false, "kind" => "ClusterRoleBinding" },
|
||||
{ "name" => "clusterroles", "namespaced" => false, "kind" => "ClusterRole" },
|
||||
{ "name" => "rolebindings", "namespaced" => true, "kind" => "RoleBinding" },
|
||||
{ "name" => "roles", "namespaced" => true, "kind" => "Role" }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue