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
8a4333ab
Commit
8a4333ab
authored
Nov 16, 2020
by
Tiger
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move deploy board model methods to core
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47147
parent
2ad9f918
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
620 additions
and
732 deletions
+620
-732
app/models/clusters/platforms/kubernetes.rb
app/models/clusters/platforms/kubernetes.rb
+62
-4
app/models/environment.rb
app/models/environment.rb
+30
-0
app/models/project_services/mock_deployment_service.rb
app/models/project_services/mock_deployment_service.rb
+22
-2
ee/app/models/ee/clusters/platforms/kubernetes.rb
ee/app/models/ee/clusters/platforms/kubernetes.rb
+0
-84
ee/app/models/ee/deployment_service.rb
ee/app/models/ee/deployment_service.rb
+0
-11
ee/app/models/ee/environment.rb
ee/app/models/ee/environment.rb
+0
-32
ee/app/models/ee/mock_deployment_service.rb
ee/app/models/ee/mock_deployment_service.rb
+0
-27
ee/spec/models/ee/clusters/platforms/kubernetes_spec.rb
ee/spec/models/ee/clusters/platforms/kubernetes_spec.rb
+0
-441
ee/spec/models/environment_spec.rb
ee/spec/models/environment_spec.rb
+0
-119
spec/models/clusters/platforms/kubernetes_spec.rb
spec/models/clusters/platforms/kubernetes_spec.rb
+387
-12
spec/models/environment_spec.rb
spec/models/environment_spec.rb
+119
-0
No files found.
app/models/clusters/platforms/kubernetes.rb
View file @
8a4333ab
...
...
@@ -94,9 +94,20 @@ module Clusters
return
unless
enabled?
pods
=
read_pods
(
environment
.
deployment_namespace
)
deployments
=
read_deployments
(
environment
.
deployment_namespace
)
# extract_relevant_pod_data avoids uploading all the pod info into ReactiveCaching
{
pods:
extract_relevant_pod_data
(
pods
)
}
ingresses
=
if
::
Feature
.
enabled?
(
:canary_ingress_weight_control
,
environment
.
project
,
default_enabled:
true
)
read_ingresses
(
environment
.
deployment_namespace
)
else
[]
end
# extract only the data required for display to avoid unnecessary caching
{
pods:
extract_relevant_pod_data
(
pods
),
deployments:
extract_relevant_deployment_data
(
deployments
),
ingresses:
extract_relevant_ingress_data
(
ingresses
)
}
end
def
terminals
(
environment
,
data
)
...
...
@@ -109,6 +120,25 @@ module Clusters
@kubeclient
||=
build_kube_client!
end
def
rollout_status
(
environment
,
data
)
project
=
environment
.
project
deployments
=
filter_by_project_environment
(
data
[
:deployments
],
project
.
full_path_slug
,
environment
.
slug
)
pods
=
filter_by_project_environment
(
data
[
:pods
],
project
.
full_path_slug
,
environment
.
slug
)
ingresses
=
data
[
:ingresses
].
presence
||
[]
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
from_deployments
(
*
deployments
,
pods_attrs:
pods
,
ingresses:
ingresses
)
end
def
ingresses
(
namespace
)
ingresses
=
read_ingresses
(
namespace
)
ingresses
.
map
{
|
ingress
|
::
Gitlab
::
Kubernetes
::
Ingress
.
new
(
ingress
)
}
end
def
patch_ingress
(
namespace
,
ingress
,
data
)
kubeclient
.
patch_ingress
(
ingress
.
name
,
data
,
namespace
)
end
private
def
default_namespace
(
project
,
environment_name
:)
...
...
@@ -140,6 +170,18 @@ module Clusters
[]
end
def
read_deployments
(
namespace
)
kubeclient
.
get_deployments
(
namespace:
namespace
).
as_json
rescue
Kubeclient
::
ResourceNotFoundError
[]
end
def
read_ingresses
(
namespace
)
kubeclient
.
get_ingresses
(
namespace:
namespace
).
as_json
rescue
Kubeclient
::
ResourceNotFoundError
[]
end
def
build_kube_client!
raise
"Incomplete settings"
unless
api_url
...
...
@@ -231,8 +273,24 @@ module Clusters
}
end
end
def
extract_relevant_deployment_data
(
deployments
)
deployments
.
map
do
|
deployment
|
{
'metadata'
=>
deployment
.
fetch
(
'metadata'
,
{}).
slice
(
'name'
,
'generation'
,
'labels'
,
'annotations'
),
'spec'
=>
deployment
.
fetch
(
'spec'
,
{}).
slice
(
'replicas'
),
'status'
=>
deployment
.
fetch
(
'status'
,
{}).
slice
(
'observedGeneration'
)
}
end
end
def
extract_relevant_ingress_data
(
ingresses
)
ingresses
.
map
do
|
ingress
|
{
'metadata'
=>
ingress
.
fetch
(
'metadata'
,
{}).
slice
(
'name'
,
'labels'
,
'annotations'
)
}
end
end
end
end
end
Clusters
::
Platforms
::
Kubernetes
.
prepend_if_ee
(
'EE::Clusters::Platforms::Kubernetes'
)
app/models/environment.rb
View file @
8a4333ab
...
...
@@ -384,8 +384,38 @@ class Environment < ApplicationRecord
!!
deployment_platform
&
.
cluster
&
.
application_elastic_stack_available?
end
def
rollout_status
return
unless
rollout_status_available?
result
=
rollout_status_with_reactive_cache
result
||
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
loading
end
def
ingresses
return
unless
rollout_status_available?
deployment_platform
.
ingresses
(
deployment_namespace
)
end
def
patch_ingress
(
ingress
,
data
)
return
unless
rollout_status_available?
deployment_platform
.
patch_ingress
(
deployment_namespace
,
ingress
,
data
)
end
private
def
rollout_status_available?
has_terminals?
end
def
rollout_status_with_reactive_cache
with_reactive_cache
do
|
data
|
deployment_platform
.
rollout_status
(
self
,
data
)
end
end
def
has_metrics_and_can_query?
has_metrics?
&&
prometheus_adapter
.
can_query?
end
...
...
app/models/project_services/mock_deployment_service.rb
View file @
8a4333ab
...
...
@@ -31,6 +31,26 @@ class MockDeploymentService < Service
def
can_test?
false
end
end
MockDeploymentService
.
prepend_if_ee
(
'EE::MockDeploymentService'
)
def
rollout_status
(
environment
)
case
environment
.
name
when
'staging'
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
new
([],
status: :not_found
)
when
'test'
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
new
([],
status: :loading
)
else
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
new
(
rollout_status_deployments
)
end
end
private
def
rollout_status_instances
data
=
File
.
read
(
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'rollout_status_instances.json'
))
Gitlab
::
Json
.
parse
(
data
)
end
def
rollout_status_deployments
[
OpenStruct
.
new
(
instances:
rollout_status_instances
)]
end
end
ee/app/models/ee/clusters/platforms/kubernetes.rb
deleted
100644 → 0
View file @
2ad9f918
# frozen_string_literal: true
module
EE
module
Clusters
module
Platforms
module
Kubernetes
extend
ActiveSupport
::
Concern
extend
::
Gitlab
::
Utils
::
Override
override
:calculate_reactive_cache_for
def
calculate_reactive_cache_for
(
environment
)
result
=
super
if
result
deployments
=
read_deployments
(
environment
.
deployment_namespace
)
ingresses
=
if
::
Feature
.
enabled?
(
:canary_ingress_weight_control
,
environment
.
project
,
default_enabled:
true
)
read_ingresses
(
environment
.
deployment_namespace
)
else
[]
end
# extract_relevant_deployment_data avoids uploading all the deployment info into ReactiveCaching
result
[
:deployments
]
=
extract_relevant_deployment_data
(
deployments
)
result
[
:ingresses
]
=
extract_relevant_ingress_data
(
ingresses
)
end
result
end
def
rollout_status
(
environment
,
data
)
project
=
environment
.
project
deployments
=
filter_by_project_environment
(
data
[
:deployments
],
project
.
full_path_slug
,
environment
.
slug
)
pods
=
filter_by_project_environment
(
data
[
:pods
],
project
.
full_path_slug
,
environment
.
slug
)
ingresses
=
data
[
:ingresses
].
presence
||
[]
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
from_deployments
(
*
deployments
,
pods_attrs:
pods
,
ingresses:
ingresses
)
end
def
ingresses
(
namespace
)
ingresses
=
read_ingresses
(
namespace
)
ingresses
.
map
{
|
ingress
|
::
Gitlab
::
Kubernetes
::
Ingress
.
new
(
ingress
)
}
end
def
patch_ingress
(
namespace
,
ingress
,
data
)
kubeclient
.
patch_ingress
(
ingress
.
name
,
data
,
namespace
)
end
private
def
read_deployments
(
namespace
)
kubeclient
.
get_deployments
(
namespace:
namespace
).
as_json
rescue
Kubeclient
::
ResourceNotFoundError
[]
end
def
read_ingresses
(
namespace
)
kubeclient
.
get_ingresses
(
namespace:
namespace
).
as_json
rescue
Kubeclient
::
ResourceNotFoundError
[]
end
def
extract_relevant_deployment_data
(
deployments
)
deployments
.
map
do
|
deployment
|
{
'metadata'
=>
deployment
.
fetch
(
'metadata'
,
{}).
slice
(
'name'
,
'generation'
,
'labels'
,
'annotations'
),
'spec'
=>
deployment
.
fetch
(
'spec'
,
{}).
slice
(
'replicas'
),
'status'
=>
deployment
.
fetch
(
'status'
,
{}).
slice
(
'observedGeneration'
)
}
end
end
def
extract_relevant_ingress_data
(
ingresses
)
ingresses
.
map
do
|
ingress
|
{
'metadata'
=>
ingress
.
fetch
(
'metadata'
,
{}).
slice
(
'name'
,
'labels'
,
'annotations'
)
}
end
end
end
end
end
end
ee/app/models/ee/deployment_service.rb
deleted
100644 → 0
View file @
2ad9f918
# frozen_string_literal: true
module
EE
module
DeploymentService
# Environments have a rollout status. This represents the current state of
# deployments to that environment.
def
rollout_status
(
environment
)
raise
NotImplementedError
end
end
end
ee/app/models/ee/environment.rb
View file @
8a4333ab
...
...
@@ -66,37 +66,5 @@ module EE
def
protected_deployable_by_user?
(
user
)
project
.
protected_environment_accessible_to?
(
name
,
user
)
end
def
rollout_status
return
unless
rollout_status_available?
result
=
rollout_status_with_reactive_cache
result
||
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
loading
end
def
ingresses
return
unless
rollout_status_available?
deployment_platform
.
ingresses
(
deployment_namespace
)
end
def
patch_ingress
(
ingress
,
data
)
return
unless
rollout_status_available?
deployment_platform
.
patch_ingress
(
deployment_namespace
,
ingress
,
data
)
end
private
def
rollout_status_available?
has_terminals?
end
def
rollout_status_with_reactive_cache
with_reactive_cache
do
|
data
|
deployment_platform
.
rollout_status
(
self
,
data
)
end
end
end
end
ee/app/models/ee/mock_deployment_service.rb
deleted
100644 → 0
View file @
2ad9f918
# frozen_string_literal: true
module
EE
module
MockDeploymentService
def
rollout_status
(
environment
)
case
environment
.
name
when
'staging'
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
new
([],
status: :not_found
)
when
'test'
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
new
([],
status: :loading
)
else
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
new
(
rollout_status_deployments
)
end
end
private
def
rollout_status_instances
data
=
File
.
read
(
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'rollout_status_instances.json'
))
Gitlab
::
Json
.
parse
(
data
)
end
def
rollout_status_deployments
[
OpenStruct
.
new
(
instances:
rollout_status_instances
)]
end
end
end
ee/spec/models/ee/clusters/platforms/kubernetes_spec.rb
deleted
100644 → 0
View file @
2ad9f918
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Clusters
::
Platforms
::
Kubernetes
do
include
KubernetesHelpers
include
ReactiveCachingHelpers
shared_examples
'resource not found error'
do
|
message
|
it
'raises error'
do
result
=
subject
expect
(
result
[
:error
]).
to
eq
(
message
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
end
end
shared_examples
'kubernetes API error'
do
|
error_code
|
it
'raises error'
do
result
=
subject
expect
(
result
[
:error
]).
to
eq
(
"Kubernetes API returned status code:
#{
error_code
}
"
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
end
end
describe
'#rollout_status'
do
let
(
:deployments
)
{
[]
}
let
(
:pods
)
{
[]
}
let
(
:ingresses
)
{
[]
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let!
(
:cluster
)
{
create
(
:cluster
,
:project
,
enabled:
true
,
platform_kubernetes:
service
)
}
let
(
:project
)
{
cluster
.
project
}
let
(
:environment
)
{
build
(
:environment
,
project:
project
,
name:
"env"
,
slug:
"env-000000"
)
}
let
(
:cache_data
)
{
Hash
(
deployments:
deployments
,
pods:
pods
,
ingresses:
ingresses
)
}
subject
(
:rollout_status
)
{
service
.
rollout_status
(
environment
,
cache_data
)
}
context
'legacy deployments based on app label'
do
let
(
:legacy_deployment
)
do
kube_deployment
(
name:
'legacy-deployment'
).
tap
do
|
deployment
|
deployment
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/env'
)
deployment
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/app'
)
deployment
[
'metadata'
][
'labels'
][
'app'
]
=
environment
.
slug
end
end
let
(
:legacy_pod
)
do
kube_pod
(
name:
'legacy-pod'
).
tap
do
|
pod
|
pod
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/env'
)
pod
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/app'
)
pod
[
'metadata'
][
'labels'
][
'app'
]
=
environment
.
slug
end
end
context
'only legacy deployments'
do
let
(
:deployments
)
{
[
legacy_deployment
]
}
let
(
:pods
)
{
[
legacy_pod
]
}
it
'contains nothing'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
).
to
eq
([])
end
end
context
'deployment with no pods'
do
let
(
:deployment
)
{
kube_deployment
(
name:
'some-deployment'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
}
let
(
:deployments
)
{
[
deployment
]
}
let
(
:pods
)
{
[]
}
it
'returns a valid status with matching deployments'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
.
map
(
&
:name
)).
to
contain_exactly
(
'some-deployment'
)
end
end
context
'new deployment based on annotations'
do
let
(
:matched_deployment
)
{
kube_deployment
(
name:
'matched-deployment'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
}
let
(
:matched_pod
)
{
kube_pod
(
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
}
let
(
:deployments
)
{
[
matched_deployment
,
legacy_deployment
]
}
let
(
:pods
)
{
[
matched_pod
,
legacy_pod
]
}
it
'contains only matching deployments'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
.
map
(
&
:name
)).
to
contain_exactly
(
'matched-deployment'
)
end
end
end
context
'with no deployments but there are pods'
do
let
(
:deployments
)
do
[]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'returns an empty array'
do
expect
(
rollout_status
.
instances
).
to
eq
([])
end
end
context
'with valid deployments'
do
let
(
:matched_deployment
)
{
kube_deployment
(
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
)
}
let
(
:unmatched_deployment
)
{
kube_deployment
}
let
(
:matched_pod
)
{
kube_pod
(
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
status:
'Pending'
)
}
let
(
:unmatched_pod
)
{
kube_pod
(
environment_slug:
environment
.
slug
+
'-test'
,
project_slug:
project
.
full_path_slug
)
}
let
(
:deployments
)
{
[
matched_deployment
,
unmatched_deployment
]
}
let
(
:pods
)
{
[
matched_pod
,
unmatched_pod
]
}
it
'creates a matching RolloutStatus'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
.
map
(
&
:annotations
)).
to
eq
([
{
'app.gitlab.com/app'
=>
project
.
full_path_slug
,
'app.gitlab.com/env'
=>
'env-000000'
}
])
expect
(
rollout_status
.
instances
).
to
eq
([{
pod_name:
"kube-pod"
,
stable:
true
,
status:
"pending"
,
tooltip:
"kube-pod (Pending)"
,
track:
"stable"
},
{
pod_name:
"Not provided"
,
stable:
true
,
status:
"pending"
,
tooltip:
"Not provided (Pending)"
,
track:
"stable"
}])
end
context
'with canary ingress'
do
let
(
:ingresses
)
{
[
kube_ingress
(
track: :canary
)]
}
it
'has canary ingress'
do
expect
(
rollout_status
).
to
be_canary_ingress_exists
expect
(
rollout_status
.
canary_ingress
.
canary_weight
).
to
eq
(
50
)
end
end
end
context
'with empty list of deployments'
do
it
'creates a matching RolloutStatus'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
).
to
be_not_found
end
end
context
'when the pod track does not match the deployment track'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'weekly'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'weekly'
),
kube_pod
(
name:
'pod-a-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'daily'
)
]
end
it
'does not return the pod'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
[
:pod_name
]
}).
to
eq
([
'pod-a-1'
])
end
end
context
'when the pod track is not stable'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'something'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'something'
)
]
end
it
'the pod is not stable'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:stable
,
:track
)
}).
to
eq
([{
stable:
false
,
track:
'something'
}])
end
end
context
'when the pod track is stable'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'stable'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'stable'
)
]
end
it
'the pod is stable'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:stable
,
:track
)
}).
to
eq
([{
stable:
true
,
track:
'stable'
}])
end
end
context
'when the pod track is not provided'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'the pod is stable'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:stable
,
:track
)
}).
to
eq
([{
stable:
true
,
track:
'stable'
}])
end
end
context
'when the number of matching pods does not match the number of replicas'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
3
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'returns a pending pod for each missing replica'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:pod_name
,
:status
)
}).
to
eq
([
{
pod_name:
'pod-a-1'
,
status:
'running'
},
{
pod_name:
'Not provided'
,
status:
'pending'
},
{
pod_name:
'Not provided'
,
status:
'pending'
}
])
end
end
context
'when pending pods are returned for missing replicas'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
,
track:
'canary'
),
kube_deployment
(
name:
'deployment-b'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
,
track:
'stable'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'canary'
)
]
end
it
'returns the correct track for the pending pods'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:pod_name
,
:status
,
:track
)
}).
to
eq
([
{
pod_name:
'pod-a-1'
,
status:
'running'
,
track:
'canary'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'canary'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'stable'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'stable'
}
])
end
end
context
'when two deployments with the same track are missing instances'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'mytrack'
),
kube_deployment
(
name:
'deployment-b'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'mytrack'
)
]
end
let
(
:pods
)
do
[]
end
it
'returns the correct number of pending pods'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:pod_name
,
:status
,
:track
)
}).
to
eq
([
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'mytrack'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'mytrack'
}
])
end
end
context
'with multiple matching deployments'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
),
kube_deployment
(
name:
'deployment-b'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-a-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-b-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-b-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'returns each pod once'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
[
:pod_name
]
}).
to
eq
([
'pod-a-1'
,
'pod-a-2'
,
'pod-b-1'
,
'pod-b-2'
])
end
end
end
describe
'#calculate_reactive_cache_for'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let
(
:namespace
)
{
'project-namespace'
}
let
(
:environment
)
{
instance_double
(
Environment
,
deployment_namespace:
namespace
,
project:
cluster
.
project
)
}
let
(
:expected_pod_cached_data
)
do
kube_pod
.
tap
{
|
kp
|
kp
[
'metadata'
].
delete
(
'namespace'
)
}
end
subject
{
service
.
calculate_reactive_cache_for
(
environment
)
}
context
'when kubernetes responds with valid deployments'
do
before
do
stub_kubeclient_pods
(
namespace
)
stub_kubeclient_deployments
(
namespace
)
stub_kubeclient_ingresses
(
namespace
)
end
shared_examples
'successful deployment request'
do
it
{
is_expected
.
to
include
(
pods:
[
expected_pod_cached_data
],
deployments:
[
kube_deployment
],
ingresses:
[
kube_ingress
])
}
end
context
'on a project level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
include_examples
'successful deployment request'
end
context
'on a group level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:group
,
platform_kubernetes:
service
)
}
include_examples
'successful deployment request'
end
context
'on an instance level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:instance
,
platform_kubernetes:
service
)
}
include_examples
'successful deployment request'
end
context
'when canary_ingress_weight_control feature flag is disabled'
do
before
do
stub_feature_flags
(
canary_ingress_weight_control:
false
)
end
it
'does not fetch ingress data from kubernetes'
do
expect
(
subject
[
:ingresses
]).
to
be_empty
end
end
end
context
'when kubernetes responds with 500s'
do
before
do
stub_kubeclient_pods
(
namespace
)
stub_kubeclient_deployments
(
namespace
,
status:
500
)
end
it
{
expect
{
subject
}.
to
raise_error
(
::
Kubeclient
::
HttpError
)
}
end
context
'when kubernetes responds with 404s'
do
before
do
stub_kubeclient_pods
(
namespace
)
stub_kubeclient_deployments
(
namespace
,
status:
404
)
stub_kubeclient_ingresses
(
namespace
,
status:
404
)
end
it
{
is_expected
.
to
include
(
deployments:
[],
ingresses:
[])
}
end
end
describe
'#ingresses'
do
subject
{
service
.
ingresses
(
namespace
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let
(
:namespace
)
{
'project-namespace'
}
context
'when there is an ingress in the namespace'
do
before
do
stub_kubeclient_ingresses
(
namespace
)
end
it
'returns an ingress'
do
expect
(
subject
.
count
).
to
eq
(
1
)
expect
(
subject
.
first
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
Ingress
)
expect
(
subject
.
first
.
name
).
to
eq
(
'production-auto-deploy'
)
end
end
context
'when there are no ingresss in the namespace'
do
before
do
allow
(
service
.
kubeclient
).
to
receive
(
:get_ingresses
)
{
raise
Kubeclient
::
ResourceNotFoundError
.
new
(
404
,
'Not found'
,
nil
)
}
end
it
'returns nothing'
do
is_expected
.
to
be_empty
end
end
end
describe
'#patch_ingress'
do
subject
{
service
.
patch_ingress
(
namespace
,
ingress
,
data
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let
(
:namespace
)
{
'project-namespace'
}
let
(
:ingress
)
{
Gitlab
::
Kubernetes
::
Ingress
.
new
(
kube_ingress
)
}
let
(
:data
)
{
{
metadata:
{
annotations:
{
name:
'test'
}
}
}
}
context
'when there is an ingress in the namespace'
do
before
do
stub_kubeclient_ingresses
(
namespace
,
method: :patch
,
resource_path:
"/
#{
ingress
.
name
}
"
)
end
it
'returns an ingress'
do
expect
(
subject
[
:items
][
0
][
:metadata
][
:name
]).
to
eq
(
'production-auto-deploy'
)
end
end
context
'when there are no ingresss in the namespace'
do
before
do
allow
(
service
.
kubeclient
).
to
receive
(
:patch_ingress
)
{
raise
Kubeclient
::
ResourceNotFoundError
.
new
(
404
,
'Not found'
,
nil
)
}
end
it
'raises an error'
do
expect
{
subject
}.
to
raise_error
(
Kubeclient
::
ResourceNotFoundError
)
end
end
end
end
ee/spec/models/environment_spec.rb
View file @
8a4333ab
...
...
@@ -184,123 +184,4 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
end
describe
'#rollout_status'
do
let!
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_user
,
projects:
[
project
])
}
let!
(
:environment
)
{
create
(
:environment
,
project:
project
)
}
let!
(
:deployment
)
{
create
(
:deployment
,
:success
,
environment:
environment
,
project:
project
)
}
subject
{
environment
.
rollout_status
}
context
'environment does not have a deployment board available'
do
before
do
allow
(
environment
).
to
receive
(
:has_terminals?
).
and_return
(
false
)
end
it
{
is_expected
.
to
be_nil
}
end
context
'cached rollout status is present'
do
let
(
:pods
)
{
%w(pod1 pod2)
}
let
(
:deployments
)
{
%w(deployment1 deployment2)
}
before
do
stub_reactive_cache
(
environment
,
pods:
pods
,
deployments:
deployments
)
end
it
'fetches the rollout status from the deployment platform'
do
expect
(
environment
.
deployment_platform
).
to
receive
(
:rollout_status
)
.
with
(
environment
,
pods:
pods
,
deployments:
deployments
)
.
and_return
(
:mock_rollout_status
)
is_expected
.
to
eq
(
:mock_rollout_status
)
end
end
context
'cached rollout status is not present yet'
do
before
do
stub_reactive_cache
(
environment
,
nil
)
end
it
'falls back to a loading status'
do
expect
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
).
to
receive
(
:loading
).
and_return
(
:mock_loading_status
)
is_expected
.
to
eq
(
:mock_loading_status
)
end
end
end
describe
'#ingresses'
do
subject
{
environment
.
ingresses
}
let
(
:deployment_platform
)
{
double
(
:deployment_platform
)
}
let
(
:deployment_namespace
)
{
'production'
}
before
do
allow
(
environment
).
to
receive
(
:deployment_platform
)
{
deployment_platform
}
allow
(
environment
).
to
receive
(
:deployment_namespace
)
{
deployment_namespace
}
end
context
'when rollout status is available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
true
}
end
it
'fetches ingresses from the deployment platform'
do
expect
(
deployment_platform
).
to
receive
(
:ingresses
).
with
(
deployment_namespace
)
subject
end
end
context
'when rollout status is not available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
false
}
end
it
'does nothing'
do
expect
(
deployment_platform
).
not_to
receive
(
:ingresses
)
subject
end
end
end
describe
'#patch_ingress'
do
subject
{
environment
.
patch_ingress
(
ingress
,
data
)
}
let
(
:ingress
)
{
double
(
:ingress
)
}
let
(
:data
)
{
double
(
:data
)
}
let
(
:deployment_platform
)
{
double
(
:deployment_platform
)
}
let
(
:deployment_namespace
)
{
'production'
}
before
do
allow
(
environment
).
to
receive
(
:deployment_platform
)
{
deployment_platform
}
allow
(
environment
).
to
receive
(
:deployment_namespace
)
{
deployment_namespace
}
end
context
'when rollout status is available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
true
}
end
it
'fetches ingresses from the deployment platform'
do
expect
(
deployment_platform
).
to
receive
(
:patch_ingress
).
with
(
deployment_namespace
,
ingress
,
data
)
subject
end
end
context
'when rollout status is not available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
false
}
end
it
'does nothing'
do
expect
(
deployment_platform
).
not_to
receive
(
:patch_ingress
)
subject
end
end
end
end
spec/models/clusters/platforms/kubernetes_spec.rb
View file @
8a4333ab
...
...
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec
.
describe
Clusters
::
Platforms
::
Kubernetes
do
include
KubernetesHelpers
include
ReactiveCachingHelpers
it
{
is_expected
.
to
belong_to
(
:cluster
)
}
it
{
is_expected
.
to
be_kind_of
(
Gitlab
::
Kubernetes
)
}
...
...
@@ -406,32 +407,62 @@ RSpec.describe Clusters::Platforms::Kubernetes do
end
describe
'#calculate_reactive_cache_for'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let
(
:namespace
)
{
'project-namespace'
}
let
(
:environment
)
{
instance_double
(
Environment
,
deployment_namespace:
namespace
,
project:
cluster
.
project
)
}
let
(
:expected_pod_cached_data
)
do
kube_pod
.
tap
{
|
kp
|
kp
[
'metadata'
].
delete
(
'namespace'
)
}
end
let
(
:namespace
)
{
"project-namespace"
}
let
(
:environment
)
{
instance_double
(
Environment
,
deployment_namespace:
namespace
,
project:
service
.
cluster
.
project
)
}
subject
{
service
.
calculate_reactive_cache_for
(
environment
)
}
context
'when
the kubernetes integration is disabled
'
do
context
'when
kubernetes responds with valid deployments
'
do
before
do
allow
(
service
).
to
receive
(
:enabled?
).
and_return
(
false
)
stub_kubeclient_pods
(
namespace
)
stub_kubeclient_deployments
(
namespace
)
stub_kubeclient_ingresses
(
namespace
)
end
it
{
is_expected
.
to
be_nil
}
shared_examples
'successful deployment request'
do
it
{
is_expected
.
to
include
(
pods:
[
expected_pod_cached_data
],
deployments:
[
kube_deployment
],
ingresses:
[
kube_ingress
])
}
end
context
'on a project level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
include_examples
'successful deployment request'
end
context
'on a group level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:group
,
platform_kubernetes:
service
)
}
include_examples
'successful deployment request'
end
context
'on an instance level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:instance
,
platform_kubernetes:
service
)
}
include_examples
'successful deployment request'
end
context
'when canary_ingress_weight_control feature flag is disabled'
do
before
do
stub_feature_flags
(
canary_ingress_weight_control:
false
)
end
it
'does not fetch ingress data from kubernetes'
do
expect
(
subject
[
:ingresses
]).
to
be_empty
end
end
end
context
'when
kubernetes responds with valid pods and deployments
'
do
context
'when
the kubernetes integration is disabled
'
do
before
do
stub_kubeclient_pods
(
namespace
)
stub_kubeclient_deployments
(
namespace
)
stub_kubeclient_ingresses
(
namespace
)
allow
(
service
).
to
receive
(
:enabled?
).
and_return
(
false
)
end
it
{
is_expected
.
to
include
(
pods:
[
expected_pod_cached_data
])
}
it
{
is_expected
.
to
be_nil
}
end
context
'when kubernetes responds with 500s'
do
...
...
@@ -451,7 +482,351 @@ RSpec.describe Clusters::Platforms::Kubernetes do
stub_kubeclient_ingresses
(
namespace
,
status:
404
)
end
it
{
is_expected
.
to
include
(
pods:
[])
}
it
{
is_expected
.
to
eq
(
pods:
[],
deployments:
[],
ingresses:
[])
}
end
end
describe
'#rollout_status'
do
let
(
:deployments
)
{
[]
}
let
(
:pods
)
{
[]
}
let
(
:ingresses
)
{
[]
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let!
(
:cluster
)
{
create
(
:cluster
,
:project
,
enabled:
true
,
platform_kubernetes:
service
)
}
let
(
:project
)
{
cluster
.
project
}
let
(
:environment
)
{
build
(
:environment
,
project:
project
,
name:
"env"
,
slug:
"env-000000"
)
}
let
(
:cache_data
)
{
Hash
(
deployments:
deployments
,
pods:
pods
,
ingresses:
ingresses
)
}
subject
(
:rollout_status
)
{
service
.
rollout_status
(
environment
,
cache_data
)
}
context
'legacy deployments based on app label'
do
let
(
:legacy_deployment
)
do
kube_deployment
(
name:
'legacy-deployment'
).
tap
do
|
deployment
|
deployment
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/env'
)
deployment
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/app'
)
deployment
[
'metadata'
][
'labels'
][
'app'
]
=
environment
.
slug
end
end
let
(
:legacy_pod
)
do
kube_pod
(
name:
'legacy-pod'
).
tap
do
|
pod
|
pod
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/env'
)
pod
[
'metadata'
][
'annotations'
].
delete
(
'app.gitlab.com/app'
)
pod
[
'metadata'
][
'labels'
][
'app'
]
=
environment
.
slug
end
end
context
'only legacy deployments'
do
let
(
:deployments
)
{
[
legacy_deployment
]
}
let
(
:pods
)
{
[
legacy_pod
]
}
it
'contains nothing'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
).
to
eq
([])
end
end
context
'deployment with no pods'
do
let
(
:deployment
)
{
kube_deployment
(
name:
'some-deployment'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
}
let
(
:deployments
)
{
[
deployment
]
}
let
(
:pods
)
{
[]
}
it
'returns a valid status with matching deployments'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
.
map
(
&
:name
)).
to
contain_exactly
(
'some-deployment'
)
end
end
context
'new deployment based on annotations'
do
let
(
:matched_deployment
)
{
kube_deployment
(
name:
'matched-deployment'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
}
let
(
:matched_pod
)
{
kube_pod
(
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
}
let
(
:deployments
)
{
[
matched_deployment
,
legacy_deployment
]
}
let
(
:pods
)
{
[
matched_pod
,
legacy_pod
]
}
it
'contains only matching deployments'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
.
map
(
&
:name
)).
to
contain_exactly
(
'matched-deployment'
)
end
end
end
context
'with no deployments but there are pods'
do
let
(
:deployments
)
do
[]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'returns an empty array'
do
expect
(
rollout_status
.
instances
).
to
eq
([])
end
end
context
'with valid deployments'
do
let
(
:matched_deployment
)
{
kube_deployment
(
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
)
}
let
(
:unmatched_deployment
)
{
kube_deployment
}
let
(
:matched_pod
)
{
kube_pod
(
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
status:
'Pending'
)
}
let
(
:unmatched_pod
)
{
kube_pod
(
environment_slug:
environment
.
slug
+
'-test'
,
project_slug:
project
.
full_path_slug
)
}
let
(
:deployments
)
{
[
matched_deployment
,
unmatched_deployment
]
}
let
(
:pods
)
{
[
matched_pod
,
unmatched_pod
]
}
it
'creates a matching RolloutStatus'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
.
map
(
&
:annotations
)).
to
eq
([
{
'app.gitlab.com/app'
=>
project
.
full_path_slug
,
'app.gitlab.com/env'
=>
'env-000000'
}
])
expect
(
rollout_status
.
instances
).
to
eq
([{
pod_name:
"kube-pod"
,
stable:
true
,
status:
"pending"
,
tooltip:
"kube-pod (Pending)"
,
track:
"stable"
},
{
pod_name:
"Not provided"
,
stable:
true
,
status:
"pending"
,
tooltip:
"Not provided (Pending)"
,
track:
"stable"
}])
end
context
'with canary ingress'
do
let
(
:ingresses
)
{
[
kube_ingress
(
track: :canary
)]
}
it
'has canary ingress'
do
expect
(
rollout_status
).
to
be_canary_ingress_exists
expect
(
rollout_status
.
canary_ingress
.
canary_weight
).
to
eq
(
50
)
end
end
end
context
'with empty list of deployments'
do
it
'creates a matching RolloutStatus'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
).
to
be_not_found
end
end
context
'when the pod track does not match the deployment track'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'weekly'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'weekly'
),
kube_pod
(
name:
'pod-a-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'daily'
)
]
end
it
'does not return the pod'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
[
:pod_name
]
}).
to
eq
([
'pod-a-1'
])
end
end
context
'when the pod track is not stable'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'something'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'something'
)
]
end
it
'the pod is not stable'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:stable
,
:track
)
}).
to
eq
([{
stable:
false
,
track:
'something'
}])
end
end
context
'when the pod track is stable'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'stable'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'stable'
)
]
end
it
'the pod is stable'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:stable
,
:track
)
}).
to
eq
([{
stable:
true
,
track:
'stable'
}])
end
end
context
'when the pod track is not provided'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'the pod is stable'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:stable
,
:track
)
}).
to
eq
([{
stable:
true
,
track:
'stable'
}])
end
end
context
'when the number of matching pods does not match the number of replicas'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
3
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'returns a pending pod for each missing replica'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:pod_name
,
:status
)
}).
to
eq
([
{
pod_name:
'pod-a-1'
,
status:
'running'
},
{
pod_name:
'Not provided'
,
status:
'pending'
},
{
pod_name:
'Not provided'
,
status:
'pending'
}
])
end
end
context
'when pending pods are returned for missing replicas'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
,
track:
'canary'
),
kube_deployment
(
name:
'deployment-b'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
,
track:
'stable'
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
track:
'canary'
)
]
end
it
'returns the correct track for the pending pods'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:pod_name
,
:status
,
:track
)
}).
to
eq
([
{
pod_name:
'pod-a-1'
,
status:
'running'
,
track:
'canary'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'canary'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'stable'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'stable'
}
])
end
end
context
'when two deployments with the same track are missing instances'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'mytrack'
),
kube_deployment
(
name:
'deployment-b'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
1
,
track:
'mytrack'
)
]
end
let
(
:pods
)
do
[]
end
it
'returns the correct number of pending pods'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
.
slice
(
:pod_name
,
:status
,
:track
)
}).
to
eq
([
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'mytrack'
},
{
pod_name:
'Not provided'
,
status:
'pending'
,
track:
'mytrack'
}
])
end
end
context
'with multiple matching deployments'
do
let
(
:deployments
)
do
[
kube_deployment
(
name:
'deployment-a'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
),
kube_deployment
(
name:
'deployment-b'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
,
replicas:
2
)
]
end
let
(
:pods
)
do
[
kube_pod
(
name:
'pod-a-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-a-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-b-1'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
),
kube_pod
(
name:
'pod-b-2'
,
environment_slug:
environment
.
slug
,
project_slug:
project
.
full_path_slug
)
]
end
it
'returns each pod once'
do
expect
(
rollout_status
.
instances
.
map
{
|
p
|
p
[
:pod_name
]
}).
to
eq
([
'pod-a-1'
,
'pod-a-2'
,
'pod-b-1'
,
'pod-b-2'
])
end
end
end
describe
'#ingresses'
do
subject
{
service
.
ingresses
(
namespace
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let
(
:namespace
)
{
'project-namespace'
}
context
'when there is an ingress in the namespace'
do
before
do
stub_kubeclient_ingresses
(
namespace
)
end
it
'returns an ingress'
do
expect
(
subject
.
count
).
to
eq
(
1
)
expect
(
subject
.
first
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
Ingress
)
expect
(
subject
.
first
.
name
).
to
eq
(
'production-auto-deploy'
)
end
end
context
'when there are no ingresss in the namespace'
do
before
do
allow
(
service
.
kubeclient
).
to
receive
(
:get_ingresses
)
{
raise
Kubeclient
::
ResourceNotFoundError
.
new
(
404
,
'Not found'
,
nil
)
}
end
it
'returns nothing'
do
is_expected
.
to
be_empty
end
end
end
describe
'#patch_ingress'
do
subject
{
service
.
patch_ingress
(
namespace
,
ingress
,
data
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let
(
:namespace
)
{
'project-namespace'
}
let
(
:ingress
)
{
Gitlab
::
Kubernetes
::
Ingress
.
new
(
kube_ingress
)
}
let
(
:data
)
{
{
metadata:
{
annotations:
{
name:
'test'
}
}
}
}
context
'when there is an ingress in the namespace'
do
before
do
stub_kubeclient_ingresses
(
namespace
,
method: :patch
,
resource_path:
"/
#{
ingress
.
name
}
"
)
end
it
'returns an ingress'
do
expect
(
subject
[
:items
][
0
][
:metadata
][
:name
]).
to
eq
(
'production-auto-deploy'
)
end
end
context
'when there are no ingresss in the namespace'
do
before
do
allow
(
service
.
kubeclient
).
to
receive
(
:patch_ingress
)
{
raise
Kubeclient
::
ResourceNotFoundError
.
new
(
404
,
'Not found'
,
nil
)
}
end
it
'raises an error'
do
expect
{
subject
}.
to
raise_error
(
Kubeclient
::
ResourceNotFoundError
)
end
end
end
end
spec/models/environment_spec.rb
View file @
8a4333ab
...
...
@@ -1421,4 +1421,123 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
end
describe
'#rollout_status'
do
let!
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_user
,
projects:
[
project
])
}
let!
(
:environment
)
{
create
(
:environment
,
project:
project
)
}
let!
(
:deployment
)
{
create
(
:deployment
,
:success
,
environment:
environment
,
project:
project
)
}
subject
{
environment
.
rollout_status
}
context
'environment does not have a deployment board available'
do
before
do
allow
(
environment
).
to
receive
(
:has_terminals?
).
and_return
(
false
)
end
it
{
is_expected
.
to
be_nil
}
end
context
'cached rollout status is present'
do
let
(
:pods
)
{
%w(pod1 pod2)
}
let
(
:deployments
)
{
%w(deployment1 deployment2)
}
before
do
stub_reactive_cache
(
environment
,
pods:
pods
,
deployments:
deployments
)
end
it
'fetches the rollout status from the deployment platform'
do
expect
(
environment
.
deployment_platform
).
to
receive
(
:rollout_status
)
.
with
(
environment
,
pods:
pods
,
deployments:
deployments
)
.
and_return
(
:mock_rollout_status
)
is_expected
.
to
eq
(
:mock_rollout_status
)
end
end
context
'cached rollout status is not present yet'
do
before
do
stub_reactive_cache
(
environment
,
nil
)
end
it
'falls back to a loading status'
do
expect
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
).
to
receive
(
:loading
).
and_return
(
:mock_loading_status
)
is_expected
.
to
eq
(
:mock_loading_status
)
end
end
end
describe
'#ingresses'
do
subject
{
environment
.
ingresses
}
let
(
:deployment_platform
)
{
double
(
:deployment_platform
)
}
let
(
:deployment_namespace
)
{
'production'
}
before
do
allow
(
environment
).
to
receive
(
:deployment_platform
)
{
deployment_platform
}
allow
(
environment
).
to
receive
(
:deployment_namespace
)
{
deployment_namespace
}
end
context
'when rollout status is available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
true
}
end
it
'fetches ingresses from the deployment platform'
do
expect
(
deployment_platform
).
to
receive
(
:ingresses
).
with
(
deployment_namespace
)
subject
end
end
context
'when rollout status is not available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
false
}
end
it
'does nothing'
do
expect
(
deployment_platform
).
not_to
receive
(
:ingresses
)
subject
end
end
end
describe
'#patch_ingress'
do
subject
{
environment
.
patch_ingress
(
ingress
,
data
)
}
let
(
:ingress
)
{
double
(
:ingress
)
}
let
(
:data
)
{
double
(
:data
)
}
let
(
:deployment_platform
)
{
double
(
:deployment_platform
)
}
let
(
:deployment_namespace
)
{
'production'
}
before
do
allow
(
environment
).
to
receive
(
:deployment_platform
)
{
deployment_platform
}
allow
(
environment
).
to
receive
(
:deployment_namespace
)
{
deployment_namespace
}
end
context
'when rollout status is available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
true
}
end
it
'fetches ingresses from the deployment platform'
do
expect
(
deployment_platform
).
to
receive
(
:patch_ingress
).
with
(
deployment_namespace
,
ingress
,
data
)
subject
end
end
context
'when rollout status is not available'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_available?
)
{
false
}
end
it
'does nothing'
do
expect
(
deployment_platform
).
not_to
receive
(
:patch_ingress
)
subject
end
end
end
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