Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
5ede567d
Commit
5ede567d
authored
Nov 02, 2018
by
Thong Kuah
Committed by
Kamil Trzciński
Nov 02, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Incorporates Kubernetes Namespace into Cluster's flow
parent
2a89f065
Changes
33
Hide whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
873 additions
and
341 deletions
+873
-341
app/models/clusters/cluster.rb
app/models/clusters/cluster.rb
+8
-0
app/models/clusters/kubernetes_namespace.rb
app/models/clusters/kubernetes_namespace.rb
+29
-6
app/models/clusters/platforms/kubernetes.rb
app/models/clusters/platforms/kubernetes.rb
+28
-8
app/models/project.rb
app/models/project.rb
+1
-1
app/models/project_services/kubernetes_service.rb
app/models/project_services/kubernetes_service.rb
+6
-1
app/services/clusters/gcp/finalize_creation_service.rb
app/services/clusters/gcp/finalize_creation_service.rb
+20
-3
app/services/clusters/gcp/kubernetes.rb
app/services/clusters/gcp/kubernetes.rb
+6
-5
app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb
...ters/gcp/kubernetes/create_or_update_namespace_service.rb
+54
-0
app/services/clusters/gcp/kubernetes/create_service_account_service.rb
...clusters/gcp/kubernetes/create_service_account_service.rb
+64
-14
app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
...clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
+5
-3
app/workers/all_queues.yml
app/workers/all_queues.yml
+1
-0
app/workers/cluster_platform_configure_worker.rb
app/workers/cluster_platform_configure_worker.rb
+22
-0
app/workers/cluster_provision_worker.rb
app/workers/cluster_provision_worker.rb
+2
-0
changelogs/unreleased/51716-create-kube-namespace.yml
changelogs/unreleased/51716-create-kube-namespace.yml
+5
-0
lib/gitlab/kubernetes/role_binding.rb
lib/gitlab/kubernetes/role_binding.rb
+6
-5
spec/controllers/projects/clusters_controller_spec.rb
spec/controllers/projects/clusters_controller_spec.rb
+11
-0
spec/factories/clusters/kubernetes_namespaces.rb
spec/factories/clusters/kubernetes_namespaces.rb
+13
-3
spec/features/projects/clusters/gcp_spec.rb
spec/features/projects/clusters/gcp_spec.rb
+1
-0
spec/features/projects/clusters/user_spec.rb
spec/features/projects/clusters/user_spec.rb
+2
-0
spec/lib/gitlab/kubernetes/role_binding_spec.rb
spec/lib/gitlab/kubernetes/role_binding_spec.rb
+2
-1
spec/models/clusters/applications/prometheus_spec.rb
spec/models/clusters/applications/prometheus_spec.rb
+8
-2
spec/models/clusters/cluster_spec.rb
spec/models/clusters/cluster_spec.rb
+1
-0
spec/models/clusters/kubernetes_namespace_spec.rb
spec/models/clusters/kubernetes_namespace_spec.rb
+54
-31
spec/models/clusters/platforms/kubernetes_spec.rb
spec/models/clusters/platforms/kubernetes_spec.rb
+34
-25
spec/models/project_services/kubernetes_service_spec.rb
spec/models/project_services/kubernetes_service_spec.rb
+3
-3
spec/models/project_spec.rb
spec/models/project_spec.rb
+13
-1
spec/services/clusters/gcp/finalize_creation_service_spec.rb
spec/services/clusters/gcp/finalize_creation_service_spec.rb
+149
-129
spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb
...gcp/kubernetes/create_or_update_namespace_service_spec.rb
+115
-0
spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb
...ers/gcp/kubernetes/create_service_account_service_spec.rb
+141
-70
spec/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service_spec.rb
...ers/gcp/kubernetes/fetch_kubernetes_token_service_spec.rb
+21
-29
spec/services/clusters/update_service_spec.rb
spec/services/clusters/update_service_spec.rb
+7
-0
spec/workers/cluster_platform_configure_worker_spec.rb
spec/workers/cluster_platform_configure_worker_spec.rb
+33
-0
spec/workers/cluster_provision_worker_spec.rb
spec/workers/cluster_provision_worker_spec.rb
+8
-1
No files found.
app/models/clusters/cluster.rb
View file @
5ede567d
...
...
@@ -19,6 +19,7 @@ module Clusters
has_many
:cluster_projects
,
class_name:
'Clusters::Project'
has_many
:projects
,
through: :cluster_projects
,
class_name:
'::Project'
has_one
:cluster_project
,
->
{
order
(
id: :desc
)
},
class_name:
'Clusters::Project'
has_many
:cluster_groups
,
class_name:
'Clusters::Group'
has_many
:groups
,
through: :cluster_groups
,
class_name:
'::Group'
...
...
@@ -128,6 +129,13 @@ module Clusters
platform_kubernetes
.
kubeclient
if
kubernetes?
end
def
find_or_initialize_kubernetes_namespace
(
cluster_project
)
kubernetes_namespaces
.
find_or_initialize_by
(
project:
cluster_project
.
project
,
cluster_project:
cluster_project
)
end
private
def
restrict_modification
...
...
app/models/clusters/kubernetes_namespace.rb
View file @
5ede567d
...
...
@@ -2,6 +2,8 @@
module
Clusters
class
KubernetesNamespace
<
ActiveRecord
::
Base
include
Gitlab
::
Kubernetes
self
.
table_name
=
'clusters_kubernetes_namespaces'
belongs_to
:cluster_project
,
class_name:
'Clusters::Project'
...
...
@@ -12,7 +14,8 @@ module Clusters
validates
:namespace
,
presence:
true
validates
:namespace
,
uniqueness:
{
scope: :cluster_id
}
before_validation
:set_namespace_and_service_account_to_default
,
on: :create
delegate
:ca_pem
,
to: :platform_kubernetes
,
allow_nil:
true
delegate
:api_url
,
to: :platform_kubernetes
,
allow_nil:
true
attr_encrypted
:service_account_token
,
mode: :per_attribute_iv
,
...
...
@@ -23,14 +26,26 @@ module Clusters
"
#{
namespace
}
-token"
end
private
def
configure_predefined_credentials
self
.
namespace
=
kubernetes_or_project_namespace
self
.
service_account_name
=
default_service_account_name
end
def
predefined_variables
config
=
YAML
.
dump
(
kubeconfig
)
def
set_namespace_and_service_account_to_default
self
.
namespace
||=
default_namespace
self
.
service_account_name
||=
default_service_account_name
Gitlab
::
Ci
::
Variables
::
Collection
.
new
.
tap
do
|
variables
|
variables
.
append
(
key:
'KUBE_SERVICE_ACCOUNT'
,
value:
service_account_name
)
.
append
(
key:
'KUBE_NAMESPACE'
,
value:
namespace
)
.
append
(
key:
'KUBE_TOKEN'
,
value:
service_account_token
,
public:
false
)
.
append
(
key:
'KUBECONFIG'
,
value:
config
,
public:
false
,
file:
true
)
end
end
def
default_namespace
private
def
kubernetes_or_project_namespace
platform_kubernetes
&
.
namespace
.
presence
||
project_namespace
end
...
...
@@ -45,5 +60,13 @@ module Clusters
def
project_slug
"
#{
project
.
path
}
-
#{
project
.
id
}
"
.
downcase
end
def
kubeconfig
to_kubeconfig
(
url:
api_url
,
namespace:
namespace
,
token:
service_account_token
,
ca_pem:
ca_pem
)
end
end
end
app/models/clusters/platforms/kubernetes.rb
View file @
5ede567d
...
...
@@ -6,6 +6,7 @@ module Clusters
include
Gitlab
::
Kubernetes
include
ReactiveCaching
include
EnumWithNil
include
AfterCommitQueue
RESERVED_NAMESPACES
=
%w(gitlab-managed-apps)
.
freeze
...
...
@@ -43,6 +44,7 @@ module Clusters
validate
:prevent_modification
,
on: :update
after_save
:clear_reactive_cache!
after_update
:update_kubernetes_namespace
alias_attribute
:ca_pem
,
:ca_cert
...
...
@@ -67,21 +69,31 @@ module Clusters
end
end
def
predefined_variables
config
=
YAML
.
dump
(
kubeconfig
)
def
predefined_variables
(
project
:)
Gitlab
::
Ci
::
Variables
::
Collection
.
new
.
tap
do
|
variables
|
variables
.
append
(
key:
'KUBE_URL'
,
value:
api_url
)
.
append
(
key:
'KUBE_TOKEN'
,
value:
token
,
public:
false
)
.
append
(
key:
'KUBE_NAMESPACE'
,
value:
actual_namespace
)
.
append
(
key:
'KUBECONFIG'
,
value:
config
,
public:
false
,
file:
true
)
variables
.
append
(
key:
'KUBE_URL'
,
value:
api_url
)
if
ca_pem
.
present?
variables
.
append
(
key:
'KUBE_CA_PEM'
,
value:
ca_pem
)
.
append
(
key:
'KUBE_CA_PEM_FILE'
,
value:
ca_pem
,
file:
true
)
end
if
kubernetes_namespace
=
cluster
.
kubernetes_namespaces
.
find_by
(
project:
project
)
variables
.
concat
(
kubernetes_namespace
.
predefined_variables
)
else
# From 11.5, every Clusters::Project should have at least one
# Clusters::KubernetesNamespace, so once migration has been completed,
# this 'else' branch will be removed. For more information, please see
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22433
config
=
YAML
.
dump
(
kubeconfig
)
variables
.
append
(
key:
'KUBE_URL'
,
value:
api_url
)
.
append
(
key:
'KUBE_TOKEN'
,
value:
token
,
public:
false
)
.
append
(
key:
'KUBE_NAMESPACE'
,
value:
actual_namespace
)
.
append
(
key:
'KUBECONFIG'
,
value:
config
,
public:
false
,
file:
true
)
end
end
end
...
...
@@ -199,6 +211,14 @@ module Clusters
true
end
def
update_kubernetes_namespace
return
unless
namespace_changed?
run_after_commit
do
ClusterPlatformConfigureWorker
.
perform_async
(
cluster_id
)
end
end
end
end
end
app/models/project.rb
View file @
5ede567d
...
...
@@ -1829,7 +1829,7 @@ class Project < ActiveRecord::Base
end
def
deployment_variables
(
environment:
nil
)
deployment_platform
(
environment:
environment
)
&
.
predefined_variables
||
[]
deployment_platform
(
environment:
environment
)
&
.
predefined_variables
(
project:
self
)
||
[]
end
def
auto_devops_variables
...
...
app/models/project_services/kubernetes_service.rb
View file @
5ede567d
...
...
@@ -104,7 +104,12 @@ class KubernetesService < DeploymentService
{
success:
false
,
result:
err
}
end
def
predefined_variables
# Project param was added on
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22011,
# as a way to keep this service compatible with
# Clusters::Platforms::Kubernetes, it won't be used on this method
# as it's only needed for Clusters::Cluster.
def
predefined_variables
(
project
:)
config
=
YAML
.
dump
(
kubeconfig
)
Gitlab
::
Ci
::
Variables
::
Collection
.
new
.
tap
do
|
variables
|
...
...
app/services/clusters/gcp/finalize_creation_service.rb
View file @
5ede567d
...
...
@@ -11,8 +11,9 @@ module Clusters
configure_provider
create_gitlab_service_account!
configure_kubernetes
cluster
.
save!
configure_project_service_account
rescue
Google
::
Apis
::
ServerError
,
Google
::
Apis
::
ClientError
,
Google
::
Apis
::
AuthorizationError
=>
e
provider
.
make_errored!
(
"Failed to request to CloudPlatform;
#{
e
.
message
}
"
)
rescue
Kubeclient
::
HttpError
=>
e
...
...
@@ -24,7 +25,10 @@ module Clusters
private
def
create_gitlab_service_account!
Clusters
::
Gcp
::
Kubernetes
::
CreateServiceAccountService
.
new
(
kube_client
,
rbac:
create_rbac_cluster?
).
execute
Clusters
::
Gcp
::
Kubernetes
::
CreateServiceAccountService
.
gitlab_creator
(
kube_client
,
rbac:
create_rbac_cluster?
).
execute
end
def
configure_provider
...
...
@@ -44,7 +48,20 @@ module Clusters
end
def
request_kubernetes_token
Clusters
::
Gcp
::
Kubernetes
::
FetchKubernetesTokenService
.
new
(
kube_client
).
execute
Clusters
::
Gcp
::
Kubernetes
::
FetchKubernetesTokenService
.
new
(
kube_client
,
Clusters
::
Gcp
::
Kubernetes
::
GITLAB_ADMIN_TOKEN_NAME
,
Clusters
::
Gcp
::
Kubernetes
::
GITLAB_SERVICE_ACCOUNT_NAMESPACE
).
execute
end
def
configure_project_service_account
kubernetes_namespace
=
cluster
.
find_or_initialize_kubernetes_namespace
(
cluster
.
cluster_project
)
Clusters
::
Gcp
::
Kubernetes
::
CreateOrUpdateNamespaceService
.
new
(
cluster:
cluster
,
kubernetes_namespace:
kubernetes_namespace
).
execute
end
def
authorization_type
...
...
app/services/clusters/gcp/kubernetes.rb
View file @
5ede567d
...
...
@@ -3,11 +3,12 @@
module
Clusters
module
Gcp
module
Kubernetes
SERVICE_ACCOUNT_NAME
=
'gitlab'
SERVICE_ACCOUNT_NAMESPACE
=
'default'
SERVICE_ACCOUNT_TOKEN_NAME
=
'gitlab-token'
CLUSTER_ROLE_BINDING_NAME
=
'gitlab-admin'
CLUSTER_ROLE_NAME
=
'cluster-admin'
GITLAB_SERVICE_ACCOUNT_NAME
=
'gitlab'
GITLAB_SERVICE_ACCOUNT_NAMESPACE
=
'default'
GITLAB_ADMIN_TOKEN_NAME
=
'gitlab-token'
GITLAB_CLUSTER_ROLE_BINDING_NAME
=
'gitlab-admin'
GITLAB_CLUSTER_ROLE_NAME
=
'cluster-admin'
PROJECT_CLUSTER_ROLE_NAME
=
'edit'
end
end
end
app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb
0 → 100644
View file @
5ede567d
# frozen_string_literal: true
module
Clusters
module
Gcp
module
Kubernetes
class
CreateOrUpdateNamespaceService
def
initialize
(
cluster
:,
kubernetes_namespace
:)
@cluster
=
cluster
@kubernetes_namespace
=
kubernetes_namespace
@platform
=
cluster
.
platform
end
def
execute
configure_kubernetes_namespace
create_project_service_account
configure_kubernetes_token
kubernetes_namespace
.
save!
rescue
::
Kubeclient
::
HttpError
=>
err
raise
err
unless
err
.
error_code
=
404
end
private
attr_reader
:cluster
,
:kubernetes_namespace
,
:platform
def
configure_kubernetes_namespace
kubernetes_namespace
.
configure_predefined_credentials
end
def
create_project_service_account
Clusters
::
Gcp
::
Kubernetes
::
CreateServiceAccountService
.
namespace_creator
(
platform
.
kubeclient
,
service_account_name:
kubernetes_namespace
.
service_account_name
,
service_account_namespace:
kubernetes_namespace
.
namespace
,
rbac:
platform
.
rbac?
).
execute
end
def
configure_kubernetes_token
kubernetes_namespace
.
service_account_token
=
fetch_service_account_token
end
def
fetch_service_account_token
Clusters
::
Gcp
::
Kubernetes
::
FetchKubernetesTokenService
.
new
(
platform
.
kubeclient
,
kubernetes_namespace
.
token_name
,
kubernetes_namespace
.
namespace
).
execute
end
end
end
end
end
app/services/clusters/gcp/kubernetes/create_service_account_service.rb
View file @
5ede567d
...
...
@@ -4,46 +4,96 @@ module Clusters
module
Gcp
module
Kubernetes
class
CreateServiceAccountService
attr_reader
:kubeclient
,
:rbac
def
initialize
(
kubeclient
,
rbac
:)
def
initialize
(
kubeclient
,
service_account_name
:,
service_account_namespace
:,
token_name
:,
rbac
:,
namespace_creator:
false
,
role_binding_name:
nil
)
@kubeclient
=
kubeclient
@service_account_name
=
service_account_name
@service_account_namespace
=
service_account_namespace
@token_name
=
token_name
@rbac
=
rbac
@namespace_creator
=
namespace_creator
@role_binding_name
=
role_binding_name
end
def
self
.
gitlab_creator
(
kubeclient
,
rbac
:)
self
.
new
(
kubeclient
,
service_account_name:
Clusters
::
Gcp
::
Kubernetes
::
GITLAB_SERVICE_ACCOUNT_NAME
,
service_account_namespace:
Clusters
::
Gcp
::
Kubernetes
::
GITLAB_SERVICE_ACCOUNT_NAMESPACE
,
token_name:
Clusters
::
Gcp
::
Kubernetes
::
GITLAB_ADMIN_TOKEN_NAME
,
rbac:
rbac
)
end
def
self
.
namespace_creator
(
kubeclient
,
service_account_name
:,
service_account_namespace
:,
rbac
:)
self
.
new
(
kubeclient
,
service_account_name:
service_account_name
,
service_account_namespace:
service_account_namespace
,
token_name:
"
#{
service_account_namespace
}
-token"
,
rbac:
rbac
,
namespace_creator:
true
,
role_binding_name:
"gitlab-
#{
service_account_namespace
}
"
)
end
def
execute
ensure_project_namespace_exists
if
namespace_creator
kubeclient
.
create_service_account
(
service_account_resource
)
kubeclient
.
create_secret
(
service_account_token_resource
)
kubeclient
.
create_cluster_role_binding
(
cluster_role_binding_resource
)
if
rbac
create_role_or_cluster_role_binding
if
rbac
end
private
attr_reader
:kubeclient
,
:service_account_name
,
:service_account_namespace
,
:token_name
,
:rbac
,
:namespace_creator
,
:role_binding_name
def
ensure_project_namespace_exists
Gitlab
::
Kubernetes
::
Namespace
.
new
(
service_account_namespace
,
kubeclient
).
ensure_exists!
end
def
create_role_or_cluster_role_binding
if
namespace_creator
kubeclient
.
create_role_binding
(
role_binding_resource
)
else
kubeclient
.
create_cluster_role_binding
(
cluster_role_binding_resource
)
end
end
def
service_account_resource
Gitlab
::
Kubernetes
::
ServiceAccount
.
new
(
service_account_name
,
service_account_namespace
).
generate
Gitlab
::
Kubernetes
::
ServiceAccount
.
new
(
service_account_name
,
service_account_namespace
).
generate
end
def
service_account_token_resource
Gitlab
::
Kubernetes
::
ServiceAccountToken
.
new
(
SERVICE_ACCOUNT_TOKEN_NAME
,
service_account_name
,
service_account_namespace
).
generate
token_name
,
service_account_name
,
service_account_namespace
).
generate
end
def
cluster_role_binding_resource
subjects
=
[{
kind:
'ServiceAccount'
,
name:
service_account_name
,
namespace:
service_account_namespace
}]
Gitlab
::
Kubernetes
::
ClusterRoleBinding
.
new
(
CLUSTER_ROLE_BINDING_NAME
,
CLUSTER_ROLE_NAME
,
C
lusters
::
Gcp
::
Kubernetes
::
GITLAB_C
LUSTER_ROLE_BINDING_NAME
,
C
lusters
::
Gcp
::
Kubernetes
::
GITLAB_C
LUSTER_ROLE_NAME
,
subjects
).
generate
end
def
service_account_name
SERVICE_ACCOUNT_NAME
end
def
service_account_namespace
SERVICE_ACCOUNT_NAMESPACE
def
role_binding_resource
Gitlab
::
Kubernetes
::
RoleBinding
.
new
(
name:
role_binding_name
,
role_name:
Clusters
::
Gcp
::
Kubernetes
::
PROJECT_CLUSTER_ROLE_NAME
,
namespace:
service_account_namespace
,
service_account_name:
service_account_name
).
generate
end
end
end
...
...
app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
View file @
5ede567d
...
...
@@ -4,10 +4,12 @@ module Clusters
module
Gcp
module
Kubernetes
class
FetchKubernetesTokenService
attr_reader
:kubeclient
attr_reader
:kubeclient
,
:service_account_token_name
,
:namespace
def
initialize
(
kubeclient
)
def
initialize
(
kubeclient
,
service_account_token_name
,
namespace
)
@kubeclient
=
kubeclient
@service_account_token_name
=
service_account_token_name
@namespace
=
namespace
end
def
execute
...
...
@@ -18,7 +20,7 @@ module Clusters
private
def
get_secret
kubeclient
.
get_secret
(
SERVICE_ACCOUNT_TOKEN_NAME
,
SERVICE_ACCOUNT_NAMESPACE
).
as_json
kubeclient
.
get_secret
(
service_account_token_name
,
namespace
).
as_json
rescue
Kubeclient
::
HttpError
=>
err
raise
err
unless
err
.
error_code
==
404
...
...
app/workers/all_queues.yml
View file @
5ede567d
...
...
@@ -28,6 +28,7 @@
-
gcp_cluster:cluster_wait_for_app_installation
-
gcp_cluster:wait_for_cluster_creation
-
gcp_cluster:cluster_wait_for_ingress_ip_address
-
gcp_cluster:cluster_platform_configure
-
github_import_advance_stage
-
github_importer:github_import_import_diff_note
...
...
app/workers/cluster_platform_configure_worker.rb
0 → 100644
View file @
5ede567d
# frozen_string_literal: true
class
ClusterPlatformConfigureWorker
include
ApplicationWorker
include
ClusterQueue
def
perform
(
cluster_id
)
Clusters
::
Cluster
.
find_by_id
(
cluster_id
).
try
do
|
cluster
|
next
unless
cluster
.
cluster_project
kubernetes_namespace
=
cluster
.
find_or_initialize_kubernetes_namespace
(
cluster
.
cluster_project
)
Clusters
::
Gcp
::
Kubernetes
::
CreateOrUpdateNamespaceService
.
new
(
cluster:
cluster
,
kubernetes_namespace:
kubernetes_namespace
).
execute
end
rescue
::
Kubeclient
::
HttpError
=>
err
Rails
.
logger
.
error
"Failed to create/update Kubernetes Namespace. id:
#{
kubernetes_namespace
.
id
}
message:
#{
err
.
message
}
"
end
end
app/workers/cluster_provision_worker.rb
View file @
5ede567d
...
...
@@ -9,6 +9,8 @@ class ClusterProvisionWorker
cluster
.
provider
.
try
do
|
provider
|
Clusters
::
Gcp
::
ProvisionService
.
new
.
execute
(
provider
)
if
cluster
.
gcp?
end
ClusterPlatformConfigureWorker
.
perform_async
(
cluster
.
id
)
if
cluster
.
user?
end
end
end
changelogs/unreleased/51716-create-kube-namespace.yml
0 → 100644
View file @
5ede567d
---
title
:
Extend RBAC by having a service account restricted to project's namespace
merge_request
:
22011
author
:
type
:
other
lib/gitlab/kubernetes/role_binding.rb
View file @
5ede567d
...
...
@@ -3,9 +3,8 @@
module
Gitlab
module
Kubernetes
class
RoleBinding
attr_reader
:role_name
,
:namespace
,
:service_account_name
def
initialize
(
role_name
:,
namespace
:,
service_account_name
:)
def
initialize
(
name
:,
role_name
:,
namespace
:,
service_account_name
:)
@name
=
name
@role_name
=
role_name
@namespace
=
namespace
@service_account_name
=
service_account_name
...
...
@@ -21,14 +20,16 @@ module Gitlab
private
attr_reader
:name
,
:role_name
,
:namespace
,
:service_account_name
def
metadata
{
name:
"gitlab-
#{
namespace
}
"
,
namespace:
namespace
}
{
name:
name
,
namespace:
namespace
}
end
def
role_ref
{
apiGroup:
'rbac.authorization.k8s.io'
,
kind:
'Role'
,
kind:
'
Cluster
Role'
,
name:
role_name
}
end
...
...
spec/controllers/projects/clusters_controller_spec.rb
View file @
5ede567d
...
...
@@ -5,6 +5,7 @@ require 'spec_helper'
describe
Projects
::
ClustersController
do
include
AccessMatchersForController
include
GoogleApi
::
CloudPlatformHelpers
include
KubernetesHelpers
set
(
:project
)
{
create
(
:project
)
}
...
...
@@ -309,6 +310,11 @@ describe Projects::ClustersController do
end
describe
'security'
do
before
do
allow
(
ClusterPlatformConfigureWorker
).
to
receive
(
:perform_async
)
stub_kubeclient_get_namespace
(
'https://kubernetes.example.com'
,
namespace:
'my-namespace'
)
end
it
{
expect
{
go
}.
to
be_allowed_for
(
:admin
)
}
it
{
expect
{
go
}.
to
be_allowed_for
(
:owner
).
of
(
project
)
}
it
{
expect
{
go
}.
to
be_allowed_for
(
:maintainer
).
of
(
project
)
}
...
...
@@ -412,6 +418,11 @@ describe Projects::ClustersController do
)
end
before
do
allow
(
ClusterPlatformConfigureWorker
).
to
receive
(
:perform_async
)
stub_kubeclient_get_namespace
(
'https://kubernetes.example.com'
,
namespace:
'my-namespace'
)
end
context
'when cluster is provided by GCP'
do
it
"updates and redirects back to show page"
do
go
...
...
spec/factories/clusters/kubernetes_namespaces.rb
View file @
5ede567d
...
...
@@ -2,8 +2,18 @@
FactoryBot
.
define
do
factory
:cluster_kubernetes_namespace
,
class:
Clusters
::
KubernetesNamespace
do
cluster
project
cluster_project
association
:cluster
,
:project
,
:provided_by_gcp
namespace
{
|
n
|
"environment
#{
n
}
"
}
after
(
:build
)
do
|
kubernetes_namespace
|
cluster_project
=
kubernetes_namespace
.
cluster
.
cluster_project
kubernetes_namespace
.
project
=
cluster_project
.
project
kubernetes_namespace
.
cluster_project
=
cluster_project
end
trait
:with_token
do
service_account_token
{
Faker
::
Lorem
.
characters
(
10
)
}
end
end
end
spec/features/projects/clusters/gcp_spec.rb
View file @
5ede567d
...
...
@@ -130,6 +130,7 @@ describe 'Gcp Cluster', :js do
context
'when user changes cluster parameters'
do
before
do
allow
(
ClusterPlatformConfigureWorker
).
to
receive
(
:perform_async
)
fill_in
'cluster_platform_kubernetes_attributes_namespace'
,
with:
'my-namespace'
page
.
within
(
'#js-cluster-details'
)
{
click_button
'Save changes'
}
end
...
...
spec/features/projects/clusters/user_spec.rb
View file @
5ede567d
...
...
@@ -9,7 +9,9 @@ describe 'User Cluster', :js do
before
do
project
.
add_maintainer
(
user
)
gitlab_sign_in
(
user
)
allow
(
Projects
::
ClustersController
).
to
receive
(
:STATUS_POLLING_INTERVAL
)
{
100
}
allow_any_instance_of
(
Clusters
::
Gcp
::
Kubernetes
::
CreateOrUpdateNamespaceService
).
to
receive
(
:execute
)
end
context
'when user does not have a cluster and visits cluster index page'
do
...
...
spec/lib/gitlab/kubernetes/role_binding_spec.rb
View file @
5ede567d
...
...
@@ -20,7 +20,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do
let
(
:role_ref
)
do
{
apiGroup:
'rbac.authorization.k8s.io'
,
kind:
'Role'
,
kind:
'
Cluster
Role'
,
name:
role_name
}
end
...
...
@@ -35,6 +35,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do
subject
do
described_class
.
new
(
name:
"gitlab-
#{
namespace
}
"
,
role_name:
role_name
,
namespace:
namespace
,
service_account_name:
service_account_name
...
...
spec/models/clusters/applications/prometheus_spec.rb
View file @
5ede567d
...
...
@@ -109,14 +109,20 @@ describe Clusters::Applications::Prometheus do
end
context
'cluster has kubeclient'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:kubernetes_url
)
{
subject
.
cluster
.
platform_kubernetes
.
api_url
}
let
(
:kube_client
)
{
subject
.
cluster
.
kubeclient
.
core_client
}
subject
{
create
(
:clusters_applications_prometheus
)
}
subject
{
create
(
:clusters_applications_prometheus
,
cluster:
cluster
)
}
before
do
subject
.
cluster
.
platform_kubernetes
.
namespace
=
'a-namespace'
stub_kubeclient_discover
(
subject
.
cluster
.
platform_kubernetes
.
api_url
)
stub_kubeclient_discover
(
cluster
.
platform_kubernetes
.
api_url
)
create
(
:cluster_kubernetes_namespace
,
cluster:
cluster
,
cluster_project:
cluster
.
cluster_project
,
project:
cluster
.
cluster_project
.
project
)
end
it
'creates proxy prometheus rest client'
do
...
...
spec/models/clusters/cluster_spec.rb
View file @
5ede567d
...
...
@@ -16,6 +16,7 @@ describe Clusters::Cluster do
it
{
is_expected
.
to
have_one
(
:application_runner
)
}
it
{
is_expected
.
to
have_many
(
:kubernetes_namespaces
)
}
it
{
is_expected
.
to
have_one
(
:kubernetes_namespace
)
}
it
{
is_expected
.
to
have_one
(
:cluster_project
)
}
it
{
is_expected
.
to
delegate_method
(
:status
).
to
(
:provider
)
}
it
{
is_expected
.
to
delegate_method
(
:status_reason
).
to
(
:provider
)
}
...
...
spec/models/clusters/kubernetes_namespace_spec.rb
View file @
5ede567d
...
...
@@ -10,23 +10,15 @@ RSpec.describe Clusters::KubernetesNamespace, type: :model do
describe
'namespace uniqueness validation'
do
let
(
:cluster_project
)
{
create
(
:cluster_project
)
}
let
(
:kubernetes_namespace
)
do
build
(
:cluster_kubernetes_namespace
,
cluster:
cluster_project
.
cluster
,
project:
cluster_project
.
project
,
cluster_project:
cluster_project
)
end
let
(
:kubernetes_namespace
)
{
build
(
:cluster_kubernetes_namespace
,
namespace:
'my-namespace'
)
}
subject
{
kubernetes_namespace
}
context
'when cluster is using the namespace'
do
before
do
create
(
:cluster_kubernetes_namespace
,
cluster:
cluster_project
.
cluster
,
project:
cluster_project
.
project
,
cluster_project:
cluster_project
,
namespace:
kubernetes_namespace
.
namespace
)
cluster:
kubernetes_namespace
.
cluster
,
namespace:
'my-namespace'
)
end
it
{
is_expected
.
not_to
be_valid
}
...
...
@@ -37,48 +29,79 @@ RSpec.describe Clusters::KubernetesNamespace, type: :model do
end
end
describe
'#set_namespace_and_service_account_to_default'
do
let
(
:cluster
)
{
platform
.
cluster
}
let
(
:cluster_project
)
{
create
(
:cluster_project
,
cluster:
cluster
)
}
let
(
:kubernetes_namespace
)
do
create
(
:cluster_kubernetes_namespace
,
cluster:
cluster_project
.
cluster
,
project:
cluster_project
.
project
,
cluster_project:
cluster_project
)
end
describe
'#configure_predefined_variables'
do
let
(
:kubernetes_namespace
)
{
build
(
:cluster_kubernetes_namespace
)
}
let
(
:cluster
)
{
kubernetes_namespace
.
cluster
}
let
(
:platform
)
{
kubernetes_namespace
.
platform_kubernetes
}
describe
'namespace'
do
let
(
:platform
)
{
create
(
:cluster_platform_kubernetes
,
namespace:
namespace
)
}
subject
{
kubernetes_namespace
.
configure_predefined_credentials
}
subject
{
kubernetes_namespace
.
namespace
}
describe
'namespace'
do
before
do
platform
.
update_column
(
:namespace
,
namespace
)
end
context
'when platform has a namespace assigned'
do
let
(
:namespace
)
{
'platform-namespace'
}
it
'should copy the namespace'
do
is_expected
.
to
eq
(
'platform-namespace'
)
subject
expect
(
kubernetes_namespace
.
namespace
).
to
eq
(
'platform-namespace'
)
end
end
context
'when platform does not have namespace assigned'
do
let
(
:project
)
{
kubernetes_namespace
.
project
}
let
(
:namespace
)
{
nil
}
let
(
:project_slug
)
{
"
#{
project
.
path
}
-
#{
project
.
id
}
"
}
it
'should
set defaul
t namespace'
do
project_slug
=
"
#{
cluster_project
.
project
.
path
}
-
#{
cluster_project
.
project_id
}
"
it
'should
fallback to projec
t namespace'
do
subject
is_expected
.
to
eq
(
project_slug
)
expect
(
kubernetes_namespace
.
namespace
)
.
to
eq
(
project_slug
)
end
end
end
describe
'service_account_name'
do
let
(
:platform
)
{
create
(
:cluster_platform_kubernetes
)
}
subject
{
kubernetes_namespace
.
service_account_name
}
let
(
:service_account_name
)
{
"
#{
kubernetes_namespace
.
namespace
}
-service-account"
}
it
'should set a service account name based on namespace'
do
is_expected
.
to
eq
(
"
#{
kubernetes_namespace
.
namespace
}
-service-account"
)
subject
expect
(
kubernetes_namespace
.
service_account_name
).
to
eq
(
service_account_name
)
end
end
end
describe
'#predefined_variables'
do
let
(
:kubernetes_namespace
)
{
create
(
:cluster_kubernetes_namespace
,
cluster:
cluster
,
service_account_token:
token
)
}
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
platform
)
}
let
(
:platform
)
{
create
(
:cluster_platform_kubernetes
,
api_url:
api_url
,
ca_cert:
ca_pem
,
token:
token
)
}
let
(
:api_url
)
{
'https://kube.domain.com'
}
let
(
:ca_pem
)
{
'CA PEM DATA'
}
let
(
:token
)
{
'token'
}
let
(
:kubeconfig
)
do
config_file
=
expand_fixture_path
(
'config/kubeconfig.yml'
)
config
=
YAML
.
safe_load
(
File
.
read
(
config_file
))
config
.
dig
(
'users'
,
0
,
'user'
)[
'token'
]
=
token
config
.
dig
(
'contexts'
,
0
,
'context'
)[
'namespace'
]
=
kubernetes_namespace
.
namespace
config
.
dig
(
'clusters'
,
0
,
'cluster'
)[
'certificate-authority-data'
]
=
Base64
.
strict_encode64
(
ca_pem
)
YAML
.
dump
(
config
)
end
it
'sets the variables'
do
expect
(
kubernetes_namespace
.
predefined_variables
).
to
include
(
{
key:
'KUBE_SERVICE_ACCOUNT'
,
value:
kubernetes_namespace
.
service_account_name
,
public:
true
},
{
key:
'KUBE_NAMESPACE'
,
value:
kubernetes_namespace
.
namespace
,
public:
true
},
{
key:
'KUBE_TOKEN'
,
value:
kubernetes_namespace
.
service_account_token
,
public:
false
},
{
key:
'KUBECONFIG'
,
value:
kubeconfig
,
public:
false
,
file:
true
}
)
end
end
end
spec/models/clusters/platforms/kubernetes_spec.rb
View file @
5ede567d
...
...
@@ -124,9 +124,17 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
describe
'#kubeclient'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
)
}
let
(
:kubernetes
)
{
build
(
:cluster_platform_kubernetes
,
:configured
,
namespace:
'a-namespace'
,
cluster:
cluster
)
}
subject
{
kubernetes
.
kubeclient
}
let
(
:kubernetes
)
{
build
(
:cluster_platform_kubernetes
,
:configured
,
namespace:
'a-namespace'
)
}
before
do
create
(
:cluster_kubernetes_namespace
,
cluster:
kubernetes
.
cluster
,
cluster_project:
kubernetes
.
cluster
.
cluster_project
,
project:
kubernetes
.
cluster
.
cluster_project
.
project
)
end
it
{
is_expected
.
to
be_an_instance_of
(
Gitlab
::
Kubernetes
::
KubeClient
)
}
end
...
...
@@ -186,29 +194,14 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
describe
'#predefined_variables'
do
let!
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
kubernetes
)
}
let
(
:kubernetes
)
{
create
(
:cluster_platform_kubernetes
,
api_url:
api_url
,
ca_cert:
ca_pem
,
token:
token
)
}
let
(
:kubernetes
)
{
create
(
:cluster_platform_kubernetes
,
api_url:
api_url
,
ca_cert:
ca_pem
)
}
let
(
:api_url
)
{
'https://kube.domain.com'
}
let
(
:ca_pem
)
{
'CA PEM DATA'
}
let
(
:token
)
{
'token'
}
let
(
:kubeconfig
)
do
config_file
=
expand_fixture_path
(
'config/kubeconfig.yml'
)
config
=
YAML
.
load
(
File
.
read
(
config_file
))
config
.
dig
(
'users'
,
0
,
'user'
)[
'token'
]
=
token
config
.
dig
(
'contexts'
,
0
,
'context'
)[
'namespace'
]
=
namespace
config
.
dig
(
'clusters'
,
0
,
'cluster'
)[
'certificate-authority-data'
]
=
Base64
.
strict_encode64
(
ca_pem
)
YAML
.
dump
(
config
)
end
shared_examples
'setting variables'
do
it
'sets the variables'
do
expect
(
kubernetes
.
predefined_variables
).
to
include
(
expect
(
kubernetes
.
predefined_variables
(
project:
cluster
.
project
)
).
to
include
(
{
key:
'KUBE_URL'
,
value:
api_url
,
public:
true
},
{
key:
'KUBE_TOKEN'
,
value:
token
,
public:
false
},
{
key:
'KUBE_NAMESPACE'
,
value:
namespace
,
public:
true
},
{
key:
'KUBECONFIG'
,
value:
kubeconfig
,
public:
false
,
file:
true
},
{
key:
'KUBE_CA_PEM'
,
value:
ca_pem
,
public:
true
},
{
key:
'KUBE_CA_PEM_FILE'
,
value:
ca_pem
,
public:
true
,
file:
true
}
)
...
...
@@ -229,13 +222,6 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
let
(
:namespace
)
{
kubernetes
.
actual_namespace
}
it_behaves_like
'setting variables'
it
'sets the KUBE_NAMESPACE'
do
kube_namespace
=
kubernetes
.
predefined_variables
.
find
{
|
h
|
h
[
:key
]
==
'KUBE_NAMESPACE'
}
expect
(
kube_namespace
).
not_to
be_nil
expect
(
kube_namespace
[
:value
]).
to
match
(
/\A
#{
Gitlab
::
PathRegex
::
PATH_REGEX_STR
}
-\d+\z/
)
end
end
end
...
...
@@ -319,4 +305,27 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it
{
is_expected
.
to
include
(
pods:
[])
}
end
end
describe
'#update_kubernetes_namespace'
do
let
(
:cluster
)
{
create
(
:cluster
,
:provided_by_gcp
)
}
let
(
:platform
)
{
cluster
.
platform
}
context
'when namespace is updated'
do
it
'should call ConfigureWorker'
do
expect
(
ClusterPlatformConfigureWorker
).
to
receive
(
:perform_async
).
with
(
cluster
.
id
).
once
platform
.
namespace
=
'new-namespace'
platform
.
save
end
end
context
'when namespace is not updated'
do
it
'should not call ConfigureWorker'
do
expect
(
ClusterPlatformConfigureWorker
).
not_to
receive
(
:perform_async
)
platform
.
username
=
"new-username"
platform
.
save
end
end
end
end
spec/models/project_services/kubernetes_service_spec.rb
View file @
5ede567d
...
...
@@ -253,7 +253,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
end
end
describe
'#predefined_variable
s
'
do
describe
'#predefined_variable'
do
let
(
:kubeconfig
)
do
config_file
=
expand_fixture_path
(
'config/kubeconfig.yml'
)
config
=
YAML
.
load
(
File
.
read
(
config_file
))
...
...
@@ -274,7 +274,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
shared_examples
'setting variables'
do
it
'sets the variables'
do
expect
(
subject
.
predefined_variables
).
to
include
(
expect
(
subject
.
predefined_variables
(
project:
project
)
).
to
include
(
{
key:
'KUBE_URL'
,
value:
'https://kube.domain.com'
,
public:
true
},
{
key:
'KUBE_TOKEN'
,
value:
'token'
,
public:
false
},
{
key:
'KUBE_NAMESPACE'
,
value:
namespace
,
public:
true
},
...
...
@@ -301,7 +301,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
it_behaves_like
'setting variables'
it
'sets the KUBE_NAMESPACE'
do
kube_namespace
=
subject
.
predefined_variables
.
find
{
|
h
|
h
[
:key
]
==
'KUBE_NAMESPACE'
}
kube_namespace
=
subject
.
predefined_variables
(
project:
project
)
.
find
{
|
h
|
h
[
:key
]
==
'KUBE_NAMESPACE'
}
expect
(
kube_namespace
).
not_to
be_nil
expect
(
kube_namespace
[
:value
]).
to
match
(
/\A
#{
Gitlab
::
PathRegex
::
PATH_REGEX_STR
}
-\d+\z/
)
...
...
spec/models/project_spec.rb
View file @
5ede567d
...
...
@@ -2405,12 +2405,24 @@ describe Project do
it_behaves_like
'same behavior between KubernetesService and Platform::Kubernetes'
end
context
'when user configured kubernetes from CI/CD > Clusters'
do
context
'when user configured kubernetes from CI/CD > Clusters
and KubernetesNamespace migration has not been executed
'
do
let!
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:project
)
{
cluster
.
project
}
it_behaves_like
'same behavior between KubernetesService and Platform::Kubernetes'
end
context
'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has been executed'
do
let!
(
:kubernetes_namespace
)
{
create
(
:cluster_kubernetes_namespace
)
}
let!
(
:cluster
)
{
kubernetes_namespace
.
cluster
}
let
(
:project
)
{
kubernetes_namespace
.
project
}
it
'should return token from kubernetes namespace'
do
expect
(
project
.
deployment_variables
).
to
include
(
{
key:
'KUBE_TOKEN'
,
value:
kubernetes_namespace
.
service_account_token
,
public:
false
}
)
end
end
end
end
...
...
spec/services/clusters/gcp/finalize_creation_service_spec.rb
View file @
5ede567d
# 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
shared_examples
'error'
do
it
'sets an error to provider object'
do
subject
it
'properly configures database models'
do
subject
expect
(
provider
.
reload
).
to
be_errored
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
)
end
it
'creates kubernetes namespace model'
do
subject
kubernetes_namespace
=
cluster
.
reload
.
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"
)
expect
(
kubernetes_namespace
.
service_account_token
).
to
be_present
end
end
shared_examples
'error'
do
it
'sets an error to provider object'
do
subject
context
'when succeeded 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'
}
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
(
gcp_project_id
,
zone
,
cluster_name
,
{
endpoint:
endpoint
,
username:
username
,
password:
password
}
)
stub_cloud_platform_get_zone_cluster_error
(
provider
.
gcp_project_id
,
provider
.
zone
,
cluster
.
name
)
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
it_behaves_like
'error'
end
context
'when token is empty'
do
let
(
:token
)
{
''
}
it_behaves_like
'error'
end
context
'when failed to fetch kubernetes token'
do
before
do
stub_kubeclient_get_secret_error
(
api_url
,
secret_name
,
namespace:
'default'
)
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:
'default'
)
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
spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb
0 → 100644
View file @
5ede567d
# frozen_string_literal: true
require
'spec_helper'
describe
Clusters
::
Gcp
::
Kubernetes
::
CreateOrUpdateNamespaceService
,
'#execute'
do
include
KubernetesHelpers
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:platform
)
{
cluster
.
platform
}
let
(
:api_url
)
{
'https://kubernetes.example.com'
}
let
(
:project
)
{
cluster
.
project
}
let
(
:cluster_project
)
{
cluster
.
cluster_project
}
subject
do
described_class
.
new
(
cluster:
cluster
,
kubernetes_namespace:
kubernetes_namespace
).
execute
end
shared_context
'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
)
stub_kubeclient_get_secret
(
api_url
,
{
metadata_name:
"
#{
namespace
}
-token"
,
token:
Base64
.
encode64
(
'sample-token'
),
namespace:
namespace
}
)
end
end
context
'when kubernetes namespace is not persisted'
do
let
(
:namespace
)
{
"
#{
project
.
path
}
-
#{
project
.
id
}
"
}
let
(
:kubernetes_namespace
)
do
build
(
:cluster_kubernetes_namespace
,
cluster:
cluster
,
project:
cluster_project
.
project
,
cluster_project:
cluster_project
)
end
include_context
'kubernetes requests'
it
'creates a Clusters::KubernetesNamespace'
do
expect
do
subject
end
.
to
change
(
Clusters
::
KubernetesNamespace
,
:count
).
by
(
1
)
end
it
'creates project service account'
do
expect_any_instance_of
(
Clusters
::
Gcp
::
Kubernetes
::
CreateServiceAccountService
).
to
receive
(
:execute
).
once
subject
end
it
'configures kubernetes token'
do
subject
kubernetes_namespace
.
reload
expect
(
kubernetes_namespace
.
namespace
).
to
eq
(
namespace
)
expect
(
kubernetes_namespace
.
service_account_name
).
to
eq
(
"
#{
namespace
}
-service-account"
)
expect
(
kubernetes_namespace
.
encrypted_service_account_token
).
to
be_present
end
end
context
'when there is a Kubernetes Namespace associated'
do
let
(
:namespace
)
{
'new-namespace'
}
let
(
:kubernetes_namespace
)
do
create
(
:cluster_kubernetes_namespace
,
cluster:
cluster
,
project:
cluster_project
.
project
,
cluster_project:
cluster_project
)
end
include_context
'kubernetes requests'
before
do
platform
.
update_column
(
:namespace
,
'new-namespace'
)
end
it
'does not create any Clusters::KubernetesNamespace'
do
subject
expect
(
cluster
.
kubernetes_namespace
).
to
eq
(
kubernetes_namespace
)
end
it
'creates project service account'
do
expect_any_instance_of
(
Clusters
::
Gcp
::
Kubernetes
::
CreateServiceAccountService
).
to
receive
(
:execute
).
once
subject
end
it
'updates Clusters::KubernetesNamespace'
do
subject
kubernetes_namespace
.
reload
expect
(
kubernetes_namespace
.
namespace
).
to
eq
(
namespace
)
expect
(
kubernetes_namespace
.
service_account_name
).
to
eq
(
"
#{
namespace
}
-service-account"
)
expect
(
kubernetes_namespace
.
encrypted_service_account_token
).
to
be_present
end
end
end
spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb
View file @
5ede567d
# frozen_string_literal: true
require
'spec_helper'
describe
Clusters
::
Gcp
::
Kubernetes
::
CreateServiceAccountService
do
include
KubernetesHelpers
let
(
:service
)
{
described_class
.
new
(
kubeclient
,
rbac:
rbac
)
}
let
(
:api_url
)
{
'http://111.111.111.111'
}
let
(
:platform_kubernetes
)
{
cluster
.
platform_kubernetes
}
let
(
:cluster_project
)
{
cluster
.
cluster_project
}
let
(
:project
)
{
cluster_project
.
project
}
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
,
auth_options:
{
username:
'admin'
,
password:
'xxx'
}
)
end
describe
'#execute'
do
let
(
:rbac
)
{
false
}
let
(
:api_url
)
{
'http://111.111.111.111'
}
let
(
:username
)
{
'admin'
}
let
(
:password
)
{
'xxx'
}
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
let
(
:kubeclient
)
do
Gitlab
::
Kubernetes
::
KubeClient
.
new
(
api_url
,
auth_options:
{
username:
username
,
password:
password
}
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
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
describe
'.gitlab_creator'
do
let
(
:namespace
)
{
'default'
}
let
(
:service_account_name
)
{
'gitlab'
}
let
(
:token_name
)
{
'gitlab-token'
}
subject
{
described_class
.
gitlab_creator
(
kubeclient
,
rbac:
rbac
).
execute
}
context
'with ABAC cluster'
do
let
(
:rbac
)
{
false
}
it_behaves_like
'creates service account and token'
end
subject
{
service
.
execute
}
context
'with RBAC cluster'
do
let
(
:rbac
)
{
true
}
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
)
end
cluster
.
platform_kubernetes
.
rbac!
shared_examples
'creates service account and token'
do
it
'creates a kubernetes service account'
do
subject
stub_kubeclient_create_cluster_role_binding
(
api_url
)
end
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
end
describe
'.namespace_creator'
do
let
(
:namespace
)
{
"
#{
project
.
path
}
-
#{
project
.
id
}
"
}
let
(
:service_account_name
)
{
"
#{
namespace
}
-service-account"
}
let
(
:token_name
)
{
"
#{
namespace
}
-token"
}
subject
do
described_class
.
namespace_creator
(
kubeclient
,
service_account_name:
service_account_name
,
service_account_namespace:
namespace
,
rbac:
rbac
).
execute
end
context
'with ABAC cluster'
do
let
(
:rbac
)
{
false
}
it_behaves_like
'creates service account and token'
end
context
'With RBAC enabled cluster'
do
let
(
:rbac
)
{
true
}
before
do
cluster
.
platform_kubernetes
.
rbac!
context
'abac enabled cluster'
do
it_behaves_like
'creates service account and token'
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:
'ClusterRole'
,
name:
'edit'
},
subjects:
[
{
kind:
'ServiceAccount'
,
name:
service_account_name
,
namespace:
namespace
}
]
)
end
)
end
end
end
...
...
spec/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service_spec.rb
View file @
5ede567d
# 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
,
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
...
...
spec/services/clusters/update_service_spec.rb
View file @
5ede567d
require
'spec_helper'
describe
Clusters
::
UpdateService
do
include
KubernetesHelpers
describe
'#execute'
do
subject
{
described_class
.
new
(
cluster
.
user
,
params
).
execute
(
cluster
)
}
...
...
@@ -34,6 +36,11 @@ describe Clusters::UpdateService do
}
end
before
do
allow
(
ClusterPlatformConfigureWorker
).
to
receive
(
:perform_async
)
stub_kubeclient_get_namespace
(
'https://kubernetes.example.com'
,
namespace:
'my-namespace'
)
end
it
'updates namespace'
do
is_expected
.
to
eq
(
true
)
expect
(
cluster
.
platform
.
namespace
).
to
eq
(
'custom-namespace'
)
...
...
spec/workers/cluster_platform_configure_worker_spec.rb
0 → 100644
View file @
5ede567d
# frozen_string_literal: true
require
'spec_helper'
describe
ClusterPlatformConfigureWorker
,
'#execute'
do
context
'when provider type is gcp'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
it
'configures kubernetes platform'
do
expect_any_instance_of
(
Clusters
::
Gcp
::
Kubernetes
::
CreateOrUpdateNamespaceService
).
to
receive
(
:execute
)
described_class
.
new
.
perform
(
cluster
.
id
)
end
end
context
'when provider type is user'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_user
)
}
it
'configures kubernetes platform'
do
expect_any_instance_of
(
Clusters
::
Gcp
::
Kubernetes
::
CreateOrUpdateNamespaceService
).
to
receive
(
:execute
)
described_class
.
new
.
perform
(
cluster
.
id
)
end
end
context
'when cluster does not exist'
do
it
'does not provision a cluster'
do
expect_any_instance_of
(
Clusters
::
Gcp
::
Kubernetes
::
CreateOrUpdateNamespaceService
).
not_to
receive
(
:execute
)
described_class
.
new
.
perform
(
123
)
end
end
end
spec/workers/cluster_provision_worker_spec.rb
View file @
5ede567d
...
...
@@ -14,18 +14,25 @@ describe ClusterProvisionWorker do
end
context
'when provider type is user'
do
let
(
:cluster
)
{
create
(
:cluster
,
provider_type: :
user
)
}
let
(
:cluster
)
{
create
(
:cluster
,
:provided_by_
user
)
}
it
'does not provision a cluster'
do
expect_any_instance_of
(
Clusters
::
Gcp
::
ProvisionService
).
not_to
receive
(
:execute
)
described_class
.
new
.
perform
(
cluster
.
id
)
end
it
'configures kubernetes platform'
do
expect
(
ClusterPlatformConfigureWorker
).
to
receive
(
:perform_async
).
with
(
cluster
.
id
)
described_class
.
new
.
perform
(
cluster
.
id
)
end
end
context
'when cluster does not exist'
do
it
'does not provision a cluster'
do
expect_any_instance_of
(
Clusters
::
Gcp
::
ProvisionService
).
not_to
receive
(
:execute
)
expect
(
ClusterPlatformConfigureWorker
).
not_to
receive
(
:perform_async
)
described_class
.
new
.
perform
(
123
)
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment