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
7eed947b
Commit
7eed947b
authored
Mar 01, 2017
by
Kamil Trzciński
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '1589-deploy-boards-backend' into 'master'
Deploy boards backend Closes #1589 See merge request !1278
parents
b33310fa
dee466ae
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
629 additions
and
71 deletions
+629
-71
app/controllers/projects/environments_controller.rb
app/controllers/projects/environments_controller.rb
+18
-1
app/models/environment.rb
app/models/environment.rb
+6
-2
app/models/project_services/deployment_service.rb
app/models/project_services/deployment_service.rb
+6
-0
app/models/project_services/kubernetes_service.rb
app/models/project_services/kubernetes_service.rb
+35
-19
app/serializers/environment_entity.rb
app/serializers/environment_entity.rb
+9
-1
app/serializers/rollout_status_entity.rb
app/serializers/rollout_status_entity.rb
+10
-0
app/serializers/rollout_status_serializer.rb
app/serializers/rollout_status_serializer.rb
+3
-0
app/views/projects/environments/_terminal_button.html.haml
app/views/projects/environments/_terminal_button.html.haml
+1
-1
changelogs/unreleased-ee/1589-deploy-boards-backend.yml
changelogs/unreleased-ee/1589-deploy-boards-backend.yml
+4
-0
config/routes/project.rb
config/routes/project.rb
+1
-0
lib/gitlab/kubernetes.rb
lib/gitlab/kubernetes.rb
+6
-6
lib/gitlab/kubernetes/deployment.rb
lib/gitlab/kubernetes/deployment.rb
+83
-0
lib/gitlab/kubernetes/rollout_status.rb
lib/gitlab/kubernetes/rollout_status.rb
+35
-0
spec/controllers/projects/environments_controller_spec.rb
spec/controllers/projects/environments_controller_spec.rb
+38
-0
spec/lib/gitlab/kubernetes/deployment_spec.rb
spec/lib/gitlab/kubernetes/deployment_spec.rb
+127
-0
spec/lib/gitlab/kubernetes/rollout_status_spec.rb
spec/lib/gitlab/kubernetes/rollout_status_spec.rb
+64
-0
spec/lib/gitlab/kubernetes_spec.rb
spec/lib/gitlab/kubernetes_spec.rb
+10
-0
spec/models/environment_spec.rb
spec/models/environment_spec.rb
+26
-4
spec/models/project_services/kubernetes_service_spec.rb
spec/models/project_services/kubernetes_service_spec.rb
+43
-29
spec/serializers/environment_entity_spec.rb
spec/serializers/environment_entity_spec.rb
+13
-1
spec/serializers/rollout_status_entity_spec.rb
spec/serializers/rollout_status_entity_spec.rb
+16
-0
spec/support/kubernetes_helpers.rb
spec/support/kubernetes_helpers.rb
+75
-7
No files found.
app/controllers/projects/environments_controller.rb
View file @
7eed947b
...
...
@@ -5,7 +5,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action
:authorize_create_deployment!
,
only:
[
:stop
]
before_action
:authorize_update_environment!
,
only:
[
:edit
,
:update
]
before_action
:authorize_admin_environment!
,
only:
[
:terminal
,
:terminal_websocket_authorize
]
before_action
:environment
,
only:
[
:show
,
:edit
,
:update
,
:stop
,
:terminal
,
:terminal_websocket_authorize
]
before_action
:environment
,
only:
[
:show
,
:edit
,
:update
,
:stop
,
:terminal
,
:terminal_websocket_authorize
,
:status
]
before_action
:verify_api_request!
,
only: :terminal_websocket_authorize
def
index
...
...
@@ -109,6 +109,23 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
end
# The rollout status of an enviroment
def
status
unless
@environment
.
deployment_service_ready?
render
text:
'Not found'
,
status:
404
return
end
rollout_status
=
@environment
.
rollout_status
if
rollout_status
.
nil?
render
body:
nil
,
status:
204
# no result yet
else
serializer
=
RolloutStatusSerializer
.
new
(
project:
@project
,
user:
@current_user
)
render
json:
serializer
.
represent
(
rollout_status
)
end
end
private
def
verify_api_request!
...
...
app/models/environment.rb
View file @
7eed947b
...
...
@@ -137,12 +137,16 @@ class Environment < ActiveRecord::Base
end
end
def
has_terminals
?
def
deployment_service_ready
?
project
.
deployment_service
.
present?
&&
available?
&&
last_deployment
.
present?
end
def
terminals
project
.
deployment_service
.
terminals
(
self
)
if
has_terminals?
project
.
deployment_service
.
terminals
(
self
)
if
deployment_service_ready?
end
def
rollout_status
project
.
deployment_service
.
rollout_status
(
self
)
if
deployment_service_ready?
end
# An environment name is not necessarily suitable for use in URLs, DNS
...
...
app/models/project_services/deployment_service.rb
View file @
7eed947b
...
...
@@ -30,4 +30,10 @@ class DeploymentService < Service
def
terminals
(
environment
)
raise
NotImplementedError
end
# Environments have a rollout status. This represents the current state of
# deployments to that environment.
def
rollout_status
(
environment
)
raise
NotImplementedError
end
end
app/models/project_services/kubernetes_service.rb
View file @
7eed947b
...
...
@@ -104,30 +104,27 @@ class KubernetesService < DeploymentService
# short time later
def
terminals
(
environment
)
with_reactive_cache
do
|
data
|
pods
=
data
.
fetch
(
:pods
,
nil
)
filter_pods
(
pods
,
app:
environment
.
slug
).
flat_map
{
|
pod
|
terminals_for_pod
(
api_url
,
namespace
,
pod
)
}.
each
{
|
terminal
|
add_terminal_auth
(
terminal
,
terminal_auth
)
}
pods
=
filter_by_label
(
data
[
:pods
],
app:
environment
.
slug
)
terminals
=
pods
.
flat_map
{
|
pod
|
terminals_for_pod
(
api_url
,
namespace
,
pod
)
}
terminals
.
each
{
|
terminal
|
add_terminal_auth
(
terminal
,
terminal_auth
)
}
end
end
# Caches all pods in the namespace so other calls don't need to block on
# network access.
def
calculate_reactive_cache
return
unless
active?
&&
project
&&
!
project
.
pending_delete?
kubeclient
=
build_kubeclient!
def
rollout_status
(
environment
)
with_reactive_cache
do
|
data
|
specs
=
filter_by_label
(
data
[
:deployments
],
app:
environment
.
slug
)
# Store as hashes, rather than as third-party types
pods
=
begin
kubeclient
.
get_pods
(
namespace:
namespace
).
as_json
rescue
KubeException
=>
err
raise
err
unless
err
.
error_code
==
404
[]
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
from_specs
(
*
specs
)
end
end
# Caches all pods & deployments in the namespace so other calls don't need to
# block on network access.
def
calculate_reactive_cache
return
unless
active?
&&
project
&&
!
project
.
pending_delete?
# We may want to cache extra things in the future
{
pods:
pod
s
}
{
pods:
read_pods
,
deployments:
read_deployment
s
}
end
private
...
...
@@ -144,6 +141,25 @@ class KubernetesService < DeploymentService
)
end
# Returns a hash of all pods in the namespace
def
read_pods
kubeclient
=
build_kubeclient!
kubeclient
.
get_pods
(
namespace:
namespace
).
as_json
rescue
KubeException
=>
err
raise
err
unless
err
.
error_code
==
404
[]
end
def
read_deployments
kubeclient
=
build_kubeclient!
(
api_path:
'apis/extensions'
,
api_version:
'v1beta1'
)
kubeclient
.
get_deployments
(
namespace:
namespace
).
as_json
rescue
KubeException
=>
err
raise
err
unless
err
.
error_code
==
404
[]
end
def
kubeclient_ssl_options
opts
=
{
verify_ssl:
OpenSSL
::
SSL
::
VERIFY_PEER
}
...
...
@@ -159,11 +175,11 @@ class KubernetesService < DeploymentService
{
bearer_token:
token
}
end
def
join_api_url
(
*
parts
)
def
join_api_url
(
api_path
)
url
=
URI
.
parse
(
api_url
)
prefix
=
url
.
path
.
sub
(
%r{/+
\z
}
,
''
)
url
.
path
=
[
prefix
,
*
parts
].
join
(
"/"
)
url
.
path
=
[
prefix
,
api_path
].
join
(
"/"
)
url
.
to_s
end
...
...
app/serializers/environment_entity.rb
View file @
7eed947b
...
...
@@ -23,7 +23,7 @@ class EnvironmentEntity < Grape::Entity
environment
)
end
expose
:terminal_path
,
if:
->
(
environment
,
_
)
{
environment
.
has_terminals
?
}
do
|
environment
|
expose
:terminal_path
,
if:
->
(
environment
,
_
)
{
environment
.
deployment_service_ready
?
}
do
|
environment
|
can?
(
request
.
user
,
:admin_environment
,
environment
.
project
)
&&
terminal_namespace_project_environment_path
(
environment
.
project
.
namespace
,
...
...
@@ -31,5 +31,13 @@ class EnvironmentEntity < Grape::Entity
environment
)
end
expose
:rollout_status_path
,
if:
->
(
environment
,
_
)
{
environment
.
deployment_service_ready?
}
do
|
environment
|
status_namespace_project_environment_path
(
environment
.
project
.
namespace
,
environment
.
project
,
environment
,
format: :json
)
end
expose
:created_at
,
:updated_at
end
app/serializers/rollout_status_entity.rb
0 → 100644
View file @
7eed947b
class
RolloutStatusEntity
<
Grape
::
Entity
include
RequestAwareEntity
expose
:instances
expose
:completion
expose
:is_completed
do
|
rollout_status
|
rollout_status
.
complete?
end
end
app/serializers/rollout_status_serializer.rb
0 → 100644
View file @
7eed947b
class
RolloutStatusSerializer
<
BaseSerializer
entity
RolloutStatusEntity
end
app/views/projects/environments/_terminal_button.html.haml
View file @
7eed947b
-
if
environment
.
has_terminals
?
&&
can?
(
current_user
,
:admin_environment
,
@project
)
-
if
environment
.
deployment_service_ready
?
&&
can?
(
current_user
,
:admin_environment
,
@project
)
=
link_to
terminal_namespace_project_environment_path
(
@project
.
namespace
,
@project
,
environment
),
class:
'btn terminal-button'
do
=
icon
(
'terminal'
)
changelogs/unreleased-ee/1589-deploy-boards-backend.yml
0 → 100644
View file @
7eed947b
---
title
:
Deploy board backend
merge_request
:
1278
author
:
config/routes/project.rb
View file @
7eed947b
...
...
@@ -191,6 +191,7 @@ constraints(ProjectUrlConstrainer.new) do
member
do
post
:stop
get
:terminal
get
:status
,
constraints:
{
format: :json
}
get
'/terminal.ws/authorize'
,
to:
'environments#terminal_websocket_authorize'
,
constraints:
{
format:
nil
}
end
...
...
lib/gitlab/kubernetes.rb
View file @
7eed947b
...
...
@@ -8,13 +8,13 @@ module Gitlab
)
# Filters an array of pods (as returned by the kubernetes API) by their labels
def
filter_
pods
(
pod
s
,
labels
=
{})
pods
.
select
do
|
pod
|
metadata
=
pod
.
fetch
(
"metadata"
,
{})
pod
_labels
=
metadata
.
fetch
(
"labels"
,
nil
)
next
unless
pod
_labels
def
filter_
by_label
(
item
s
,
labels
=
{})
items
.
select
do
|
item
|
metadata
=
item
.
fetch
(
"metadata"
,
{})
item
_labels
=
metadata
.
fetch
(
"labels"
,
nil
)
next
unless
item
_labels
labels
.
all?
{
|
k
,
v
|
pod
_labels
[
k
.
to_s
]
==
v
}
labels
.
all?
{
|
k
,
v
|
item
_labels
[
k
.
to_s
]
==
v
}
end
end
...
...
lib/gitlab/kubernetes/deployment.rb
0 → 100644
View file @
7eed947b
module
Gitlab
module
Kubernetes
class
Deployment
def
initialize
(
attributes
=
{})
@attributes
=
attributes
end
def
name
metadata
[
'name'
]
end
def
labels
metadata
[
'labels'
]
end
def
outdated?
observed_generation
<
generation
end
def
wanted_replicas
spec
.
fetch
(
'replicas'
,
0
)
end
def
finished_replicas
status
.
fetch
(
'availableReplicas'
,
0
)
end
def
deploying_replicas
updated_replicas
-
finished_replicas
end
def
waiting_replicas
wanted_replicas
-
updated_replicas
end
def
instances
return
deployment_instances
(
wanted_replicas
,
'unknown'
,
'waiting'
)
if
name
.
nil?
return
deployment_instances
(
wanted_replicas
,
name
,
'waiting'
)
if
outdated?
out
=
deployment_instances
(
finished_replicas
,
name
,
'finished'
)
out
.
push
(
*
deployment_instances
(
deploying_replicas
,
name
,
'deploying'
,
out
.
size
))
out
.
push
(
*
deployment_instances
(
waiting_replicas
,
name
,
'waiting'
,
out
.
size
))
out
end
private
def
deployment_instances
(
n
,
name
,
status
,
offset
=
0
)
return
[]
if
n
<
0
Array
.
new
(
n
)
{
|
idx
|
deployment_instance
(
idx
+
offset
,
name
,
status
)
}
end
def
deployment_instance
(
n
,
name
,
status
)
{
status:
status
,
tooltip:
"
#{
name
}
(pod
#{
n
}
)
#{
status
.
capitalize
}
"
}
end
def
metadata
@attributes
.
fetch
(
'metadata'
,
{})
end
def
spec
@attributes
.
fetch
(
'spec'
,
{})
end
def
status
@attributes
.
fetch
(
'status'
,
{})
end
def
updated_replicas
status
.
fetch
(
'updatedReplicas'
,
0
)
end
def
generation
metadata
.
fetch
(
'generation'
,
0
)
end
def
observed_generation
status
.
fetch
(
'observedGeneration'
,
0
)
end
end
end
end
lib/gitlab/kubernetes/rollout_status.rb
0 → 100644
View file @
7eed947b
module
Gitlab
module
Kubernetes
# Calculates the rollout status for a set of kubernetes deployments.
#
# A GitLab environment may be composed of several Kubernetes deployments and
# other resources, unified by an `app=` label. The rollout status sums the
# Kubernetes deployments together.
class
RolloutStatus
attr_reader
:deployments
,
:instances
,
:completion
def
complete?
completion
==
100
end
def
self
.
from_specs
(
*
specs
)
deployments
=
specs
.
map
{
|
spec
|
::
Gitlab
::
Kubernetes
::
Deployment
.
new
(
spec
)
}
new
(
deployments
)
end
def
initialize
(
deployments
)
@deployments
=
deployments
@instances
=
deployments
.
flat_map
(
&
:instances
)
@completion
=
if
@instances
.
empty?
100
else
finished
=
@instances
.
select
{
|
instance
|
instance
[
:status
]
==
'finished'
}.
count
(
finished
/
@instances
.
count
.
to_f
*
100
).
to_i
end
end
end
end
end
spec/controllers/projects/environments_controller_spec.rb
View file @
7eed947b
...
...
@@ -187,6 +187,44 @@ describe Projects::EnvironmentsController do
end
end
describe
'GET #status'
do
context
'without deployment service'
do
it
'returns 404'
do
get
:status
,
environment_params
expect
(
response
.
status
).
to
eq
(
404
)
end
end
context
'with deployment service'
do
let
(
:project
)
{
create
(
:kubernetes_project
)
}
before
do
allow_any_instance_of
(
Environment
).
to
receive
(
:deployment_service_ready?
).
and_return
(
true
)
end
it
'returns 204 until the rollout status is present'
do
expect_any_instance_of
(
Environment
).
to
receive
(
:rollout_status
).
and_return
(
nil
)
get
:status
,
environment_params
expect
(
response
.
status
).
to
eq
(
204
)
end
it
'returns the rollout status when present'
do
expect_any_instance_of
(
Environment
).
to
receive
(
:rollout_status
).
and_return
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
new
([]))
get
:status
,
environment_params
expect
(
response
.
status
).
to
eq
(
200
)
end
end
end
def
environment_params
(
opts
=
{})
opts
.
reverse_merge
(
namespace_id:
project
.
namespace
,
project_id:
project
,
...
...
spec/lib/gitlab/kubernetes/deployment_spec.rb
0 → 100644
View file @
7eed947b
require
'spec_helper'
describe
Gitlab
::
Kubernetes
::
Deployment
do
subject
(
:deployment
)
{
described_class
.
new
(
params
)
}
describe
'#name'
do
let
(
:params
)
{
named
(
:selected
)
}
it
{
expect
(
deployment
.
name
).
to
eq
(
:selected
)
}
end
describe
'#labels'
do
let
(
:params
)
{
make
(
'metadata'
,
'labels'
=>
:selected
)
}
it
{
expect
(
deployment
.
labels
).
to
eq
(
:selected
)
}
end
describe
'#outdated?'
do
context
'when outdated'
do
let
(
:params
)
{
generation
(
2
,
1
)
}
it
{
expect
(
deployment
.
outdated?
).
to
be_truthy
}
end
context
'when up to date'
do
let
(
:params
)
{
generation
(
2
,
2
)
}
it
{
expect
(
deployment
.
outdated?
).
to
be_falsy
}
end
context
'when ahead of latest'
do
let
(
:params
)
{
generation
(
1
,
2
)
}
it
{
expect
(
deployment
.
outdated?
).
to
be_falsy
}
end
end
describe
'#wanted_replicas'
do
let
(
:params
)
{
make
(
'spec'
,
'replicas'
=>
:selected
)
}
it
{
expect
(
deployment
.
wanted_replicas
).
to
eq
(
:selected
)
}
end
describe
'#finished_replicas'
do
let
(
:params
)
{
make
(
'status'
,
'availableReplicas'
=>
:selected
)
}
it
{
expect
(
deployment
.
finished_replicas
).
to
eq
(
:selected
)
}
end
describe
'#deploying_replicas'
do
let
(
:params
)
{
make
(
'status'
,
'availableReplicas'
=>
2
,
'updatedReplicas'
=>
4
)
}
it
{
expect
(
deployment
.
deploying_replicas
).
to
eq
(
2
)
}
end
describe
'#waiting_replicas'
do
let
(
:params
)
{
combine
(
make
(
'spec'
,
'replicas'
=>
4
),
make
(
'status'
,
'updatedReplicas'
=>
2
))
}
it
{
expect
(
deployment
.
waiting_replicas
).
to
eq
(
2
)
}
end
describe
'#instances'
do
context
'when unnamed'
do
let
(
:params
)
{
combine
(
generation
(
1
,
1
),
instances
)
}
it
'returns all instances as unknown and waiting'
do
expected
=
[
{
status:
'waiting'
,
tooltip:
'unknown (pod 0) Waiting'
},
{
status:
'waiting'
,
tooltip:
'unknown (pod 1) Waiting'
},
{
status:
'waiting'
,
tooltip:
'unknown (pod 2) Waiting'
},
{
status:
'waiting'
,
tooltip:
'unknown (pod 3) Waiting'
},
]
expect
(
deployment
.
instances
).
to
eq
(
expected
)
end
end
context
'when outdated'
do
let
(
:params
)
{
combine
(
named
(
'foo'
),
generation
(
1
,
0
),
instances
)
}
it
'returns all instances as named and waiting'
do
expected
=
[
{
status:
'waiting'
,
tooltip:
'foo (pod 0) Waiting'
},
{
status:
'waiting'
,
tooltip:
'foo (pod 1) Waiting'
},
{
status:
'waiting'
,
tooltip:
'foo (pod 2) Waiting'
},
{
status:
'waiting'
,
tooltip:
'foo (pod 3) Waiting'
},
]
expect
(
deployment
.
instances
).
to
eq
(
expected
)
end
end
context
'with pods of each type'
do
let
(
:params
)
{
combine
(
named
(
'foo'
),
generation
(
1
,
1
),
instances
)
}
it
'returns all instances'
do
expected
=
[
{
status:
'finished'
,
tooltip:
'foo (pod 0) Finished'
},
{
status:
'deploying'
,
tooltip:
'foo (pod 1) Deploying'
},
{
status:
'waiting'
,
tooltip:
'foo (pod 2) Waiting'
},
{
status:
'waiting'
,
tooltip:
'foo (pod 3) Waiting'
},
]
expect
(
deployment
.
instances
).
to
eq
(
expected
)
end
end
end
def
generation
(
expected
,
observed
)
combine
(
make
(
'metadata'
,
'generation'
=>
expected
),
make
(
'status'
,
'observedGeneration'
=>
observed
)
)
end
def
named
(
name
=
"foo"
)
make
(
'metadata'
,
'name'
=>
name
)
end
def
instances
combine
(
make
(
'spec'
,
'replicas'
=>
4
),
make
(
'status'
,
'availableReplicas'
=>
1
,
'updatedReplicas'
=>
2
),
)
end
def
make
(
key
,
values
=
{})
hsh
=
{}
hsh
[
key
]
=
values
hsh
end
def
combine
(
*
hashes
)
out
=
{}
hashes
.
each
{
|
hsh
|
out
=
out
.
deep_merge
(
hsh
)
}
out
end
end
spec/lib/gitlab/kubernetes/rollout_status_spec.rb
0 → 100644
View file @
7eed947b
require
'spec_helper'
describe
Gitlab
::
Kubernetes
::
RolloutStatus
do
include
KubernetesHelpers
let
(
:specs_all_finished
)
{
[
kube_deployment
(
name:
'one'
),
kube_deployment
(
name:
'two'
)]
}
let
(
:specs_half_finished
)
do
[
kube_deployment
(
name:
'one'
),
kube_deployment
(
name:
'two'
).
deep_merge
(
'status'
=>
{
'availableReplicas'
=>
0
})
]
end
let
(
:specs
)
{
specs_all_finished
}
subject
(
:rollout_status
)
{
described_class
.
from_specs
(
*
specs
)
}
describe
'#deployments'
do
it
'stores the deployments'
do
expect
(
rollout_status
.
deployments
).
to
be_kind_of
(
Array
)
expect
(
rollout_status
.
deployments
.
size
).
to
eq
(
2
)
expect
(
rollout_status
.
deployments
.
first
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
Deployment
)
end
end
describe
'#instances'
do
it
'stores the union of deployment instances'
do
expected
=
[
{
status:
'finished'
,
tooltip:
'one (pod 0) Finished'
},
{
status:
'finished'
,
tooltip:
'one (pod 1) Finished'
},
{
status:
'finished'
,
tooltip:
'one (pod 2) Finished'
},
{
status:
'finished'
,
tooltip:
'two (pod 0) Finished'
},
{
status:
'finished'
,
tooltip:
'two (pod 1) Finished'
},
{
status:
'finished'
,
tooltip:
'two (pod 2) Finished'
},
]
expect
(
rollout_status
.
instances
).
to
eq
(
expected
)
end
end
describe
'#completion'
do
subject
{
rollout_status
.
completion
}
context
'when all instances are finished'
do
it
{
is_expected
.
to
eq
(
100
)
}
end
context
'when half of the instances are finished'
do
let
(
:specs
)
{
specs_half_finished
}
it
{
is_expected
.
to
eq
(
50
)
}
end
end
describe
'#complete?'
do
subject
{
rollout_status
.
complete?
}
context
'when all instances are finished'
do
it
{
is_expected
.
to
be_truthy
}
end
context
'when half of the instances are finished'
do
let
(
:specs
)
{
specs_half_finished
}
it
{
is_expected
.
to
be_falsy
}
end
end
end
spec/lib/gitlab/kubernetes_spec.rb
View file @
7eed947b
require
'spec_helper'
describe
Gitlab
::
Kubernetes
do
include
KubernetesHelpers
include
described_class
describe
'#container_exec_url'
do
...
...
@@ -36,4 +37,13 @@ describe Gitlab::Kubernetes do
it
{
expect
(
result
.
query
).
to
match
(
/\Acontainer=container\+1&/
)
}
end
end
describe
'#filter_by_label'
do
it
'returns matching labels'
do
matching_items
=
[
kube_pod
(
app:
'foo'
),
kube_deployment
(
app:
'foo'
)]
items
=
matching_items
+
[
kube_pod
,
kube_deployment
]
expect
(
filter_by_label
(
items
,
app:
'foo'
)).
to
eq
(
matching_items
)
end
end
end
spec/models/environment_spec.rb
View file @
7eed947b
...
...
@@ -247,8 +247,8 @@ describe Environment, models: true do
end
end
describe
'#
has_terminals
?'
do
subject
{
environment
.
has_terminals
?
}
describe
'#
deployment_service_ready
?'
do
subject
{
environment
.
deployment_service_ready
?
}
context
'when the enviroment is available'
do
context
'with a deployment service'
do
...
...
@@ -281,7 +281,7 @@ describe Environment, models: true do
subject
{
environment
.
terminals
}
context
'when the environment has terminals'
do
before
{
allow
(
environment
).
to
receive
(
:
has_terminals
?
).
and_return
(
true
)
}
before
{
allow
(
environment
).
to
receive
(
:
deployment_service_ready
?
).
and_return
(
true
)
}
it
'returns the terminals from the deployment service'
do
expect
(
project
.
deployment_service
).
...
...
@@ -293,7 +293,29 @@ describe Environment, models: true do
end
context
'when the environment does not have terminals'
do
before
{
allow
(
environment
).
to
receive
(
:has_terminals?
).
and_return
(
false
)
}
before
{
allow
(
environment
).
to
receive
(
:deployment_service_ready?
).
and_return
(
false
)
}
it
{
is_expected
.
to
eq
(
nil
)
}
end
end
describe
'#rollout_status'
do
let
(
:project
)
{
create
(
:kubernetes_project
)
}
subject
{
environment
.
rollout_status
}
context
'when the environment has rollout status'
do
before
{
allow
(
environment
).
to
receive
(
:deployment_service_ready?
).
and_return
(
true
)
}
it
'returns the rollout status from the deployment service'
do
expect
(
project
.
deployment_service
).
to
receive
(
:rollout_status
).
with
(
environment
).
and_return
(
:fake_rollout_status
)
is_expected
.
to
eq
(
:fake_rollout_status
)
end
end
context
'when the environment does not have rollout status'
do
before
{
allow
(
environment
).
to
receive
(
:deployment_service_ready?
).
and_return
(
false
)
}
it
{
is_expected
.
to
eq
(
nil
)
}
end
end
...
...
spec/models/project_services/kubernetes_service_spec.rb
View file @
7eed947b
...
...
@@ -7,24 +7,6 @@ describe KubernetesService, models: true, caching: true do
let
(
:project
)
{
create
(
:kubernetes_project
)
}
let
(
:service
)
{
project
.
kubernetes_service
}
# We use Kubeclient to interactive with the Kubernetes API. It will
# GET /api/v1 for a list of resources the API supports. This must be stubbed
# in addition to any other HTTP requests we expect it to perform.
let
(
:discovery_url
)
{
service
.
api_url
+
'/api/v1'
}
let
(
:discovery_response
)
{
{
body:
kube_discovery_body
.
to_json
}
}
let
(
:pods_url
)
{
service
.
api_url
+
"/api/v1/namespaces/
#{
service
.
namespace
}
/pods"
}
let
(
:pods_response
)
{
{
body:
kube_pods_body
(
kube_pod
).
to_json
}
}
def
stub_kubeclient_discover
WebMock
.
stub_request
(
:get
,
discovery_url
).
to_return
(
discovery_response
)
end
def
stub_kubeclient_pods
stub_kubeclient_discover
WebMock
.
stub_request
(
:get
,
pods_url
).
to_return
(
pods_response
)
end
describe
"Associations"
do
it
{
is_expected
.
to
belong_to
:project
}
end
...
...
@@ -87,6 +69,8 @@ describe KubernetesService, models: true, caching: true do
end
describe
'#test'
do
let
(
:discovery_url
)
{
'https://kubernetes.example.com/api/v1'
}
before
do
stub_kubeclient_discover
end
...
...
@@ -95,7 +79,8 @@ describe KubernetesService, models: true, caching: true do
let
(
:discovery_url
)
{
'https://kubernetes.example.com/prefix/api/v1'
}
it
'tests with the prefix'
do
service
.
api_url
=
'https://kubernetes.example.com/prefix/'
service
.
api_url
=
'https://kubernetes.example.com/prefix'
stub_kubeclient_discover
expect
(
service
.
test
[
:success
]).
to
be_truthy
expect
(
WebMock
).
to
have_requested
(
:get
,
discovery_url
).
once
...
...
@@ -123,9 +108,9 @@ describe KubernetesService, models: true, caching: true do
end
context
'failure'
do
let
(
:discovery_response
)
{
{
status:
404
}
}
it
'fails to read the discovery endpoint'
do
WebMock
.
stub_request
(
:get
,
service
.
api_url
+
'/api/v1'
).
to_return
(
status:
404
)
expect
(
service
.
test
[
:success
]).
to
be_falsy
expect
(
WebMock
).
to
have_requested
(
:get
,
discovery_url
).
once
end
...
...
@@ -201,8 +186,26 @@ describe KubernetesService, models: true, caching: true do
end
end
describe
'#rollout_status'
do
let
(
:environment
)
{
build
(
:environment
,
project:
project
,
name:
"env"
,
slug:
"env-000000"
)
}
subject
(
:rollout_status
)
{
service
.
rollout_status
(
environment
)
}
context
'with valid deployments'
do
before
do
stub_reactive_cache
(
service
,
deployments:
[
kube_deployment
(
app:
environment
.
slug
),
kube_deployment
]
)
end
it
'creates a matching RolloutStatus'
do
expect
(
rollout_status
).
to
be_kind_of
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
)
expect
(
rollout_status
.
deployments
.
map
(
&
:labels
)).
to
eq
([{
'app'
=>
'env-000000'
}])
end
end
end
describe
'#calculate_reactive_cache'
do
before
{
stub_kubeclient_pods
}
subject
{
service
.
calculate_reactive_cache
}
context
'when service is inactive'
do
...
...
@@ -211,20 +214,31 @@ describe KubernetesService, models: true, caching: true do
it
{
is_expected
.
to
be_nil
}
end
context
'when kubernetes responds with valid pods'
do
it
{
is_expected
.
to
eq
(
pods:
[
kube_pod
])
}
context
'when kubernetes responds with valid pods and deployments'
do
before
do
stub_kubeclient_pods
stub_kubeclient_deployments
end
it
{
is_expected
.
to
eq
(
pods:
[
kube_pod
],
deployments:
[
kube_deployment
])
}
end
context
'when kubernetes responds with 500'
do
let
(
:pods_response
)
{
{
status:
500
}
}
context
'when kubernetes responds with 500s'
do
before
do
stub_kubeclient_pods
(
status:
500
)
stub_kubeclient_deployments
(
status:
500
)
end
it
{
expect
{
subject
}.
to
raise_error
(
KubeException
)
}
end
context
'when kubernetes responds with 404'
do
let
(
:pods_response
)
{
{
status:
404
}
}
context
'when kubernetes responds with 404s'
do
before
do
stub_kubeclient_pods
(
status:
404
)
stub_kubeclient_deployments
(
status:
404
)
end
it
{
is_expected
.
to
eq
(
pods:
[])
}
it
{
is_expected
.
to
eq
(
pods:
[]
,
deployments:
[]
)
}
end
end
end
spec/serializers/environment_entity_spec.rb
View file @
7eed947b
...
...
@@ -2,7 +2,7 @@ require 'spec_helper'
describe
EnvironmentEntity
do
let
(
:entity
)
do
described_class
.
new
(
environment
,
request:
double
)
described_class
.
new
(
environment
,
request:
double
(
user:
nil
)
)
end
let
(
:environment
)
{
create
(
:environment
)
}
...
...
@@ -15,4 +15,16 @@ describe EnvironmentEntity do
it
'exposes core elements of environment'
do
expect
(
subject
).
to
include
(
:id
,
:name
,
:state
,
:environment_path
)
end
context
'with deployment service ready'
do
before
do
allow
(
environment
).
to
receive
(
:deployment_service_ready?
).
and_return
(
true
)
end
it
'exposes rollout_status_path'
do
expected
=
'/'
+
[
environment
.
project
.
full_path
,
'environments'
,
environment
.
id
,
'status.json'
].
join
(
'/'
)
expect
(
subject
[
:rollout_status_path
]).
to
eq
(
expected
)
end
end
end
spec/serializers/rollout_status_entity_spec.rb
0 → 100644
View file @
7eed947b
require
'spec_helper'
describe
RolloutStatusEntity
do
include
KubernetesHelpers
let
(
:entity
)
do
described_class
.
new
(
rollout_status
,
request:
double
)
end
let
(
:rollout_status
)
{
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
from_specs
(
kube_deployment
)
}
subject
{
entity
.
as_json
}
it
{
is_expected
.
to
have_key
(
:instances
)
}
it
{
is_expected
.
to
have_key
(
:completion
)
}
it
{
is_expected
.
to
have_key
(
:is_completed
)
}
end
spec/support/kubernetes_helpers.rb
View file @
7eed947b
module
KubernetesHelpers
include
Gitlab
::
Kubernetes
def
kube_discovery_body
def
kube_response
(
body
)
{
body:
body
.
to_json
}
end
def
kube_pods_response
kube_response
(
kube_pods_body
)
end
def
kube_deployments_response
kube_response
(
kube_deployments_body
)
end
def
stub_kubeclient_discover
WebMock
.
stub_request
(
:get
,
service
.
api_url
+
'/api/v1'
).
to_return
(
kube_response
(
kube_v1_discovery_body
))
WebMock
.
stub_request
(
:get
,
service
.
api_url
+
'/apis/extensions/v1beta1'
).
to_return
(
kube_response
(
kube_v1beta1_discovery_body
))
end
def
stub_kubeclient_pods
(
response
=
nil
)
stub_kubeclient_discover
pods_url
=
service
.
api_url
+
"/api/v1/namespaces/
#{
service
.
namespace
}
/pods"
WebMock
.
stub_request
(
:get
,
pods_url
).
to_return
(
response
||
kube_pods_response
)
end
def
stub_kubeclient_deployments
(
response
=
nil
)
stub_kubeclient_discover
deployments_url
=
service
.
api_url
+
"/apis/extensions/v1beta1/namespaces/
#{
service
.
namespace
}
/deployments"
WebMock
.
stub_request
(
:get
,
deployments_url
).
to_return
(
response
||
kube_deployments_response
)
end
def
kube_v1_discovery_body
{
"kind"
=>
"APIResourceList"
,
"resources"
=>
[
{
"name"
=>
"pods"
,
"namespaced"
=>
true
,
"kind"
=>
"Pod"
},
]
{
"name"
=>
"deployments"
,
"namespaced"
=>
true
,
"kind"
=>
"Deployment"
},
],
}
end
def
kube_pods_body
(
*
pods
)
{
"kind"
=>
"PodList"
,
"items"
=>
[
kube_pod
]
}
def
kube_v1beta1_discovery_body
{
"kind"
=>
"APIResourceList"
,
"resources"
=>
[
{
"name"
=>
"pods"
,
"namespaced"
=>
true
,
"kind"
=>
"Pod"
},
{
"name"
=>
"deployments"
,
"namespaced"
=>
true
,
"kind"
=>
"Deployment"
},
],
}
end
def
kube_pods_body
{
"kind"
=>
"PodList"
,
"items"
=>
[
kube_pod
]
}
end
def
kube_deployments_body
{
"kind"
=>
"DeploymentList"
,
"items"
=>
[
kube_deployment
]
}
end
# This is a partial response, it will have many more elements in reality but
# these are the ones we care about at the moment
def
kube_pod
(
app:
"valid-pod-label"
)
def
kube_pod
(
name:
"kube-pod"
,
app:
"valid-pod-label"
)
{
"metadata"
=>
{
"name"
=>
"kube-pod"
,
"name"
=>
name
,
"creationTimestamp"
=>
"2016-11-25T19:55:19Z"
,
"labels"
=>
{
"app"
=>
app
},
},
...
...
@@ -34,6 +85,23 @@ module KubernetesHelpers
}
end
def
kube_deployment
(
name:
"kube-deployment"
,
app:
"valid-deployment-label"
)
{
"metadata"
=>
{
"name"
=>
name
,
"generation"
=>
4
,
"labels"
=>
{
"app"
=>
app
},
},
"spec"
=>
{
"replicas"
=>
3
},
"status"
=>
{
"observedGeneration"
=>
4
,
"replicas"
=>
3
,
"updatedReplicas"
=>
3
,
"availableReplicas"
=>
3
,
},
}
end
def
kube_terminals
(
service
,
pod
)
pod_name
=
pod
[
'metadata'
][
'name'
]
containers
=
pod
[
'spec'
][
'containers'
]
...
...
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