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
fc6e3515
Commit
fc6e3515
authored
Jun 13, 2017
by
Nick Thomas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Backport EE changes to the Kubernetes service
parent
fbfddd99
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
107 additions
and
57 deletions
+107
-57
app/models/project_services/kubernetes_service.rb
app/models/project_services/kubernetes_service.rb
+18
-19
lib/gitlab/kubernetes.rb
lib/gitlab/kubernetes.rb
+6
-6
spec/lib/gitlab/kubernetes_spec.rb
spec/lib/gitlab/kubernetes_spec.rb
+10
-0
spec/models/project_services/kubernetes_service_spec.rb
spec/models/project_services/kubernetes_service_spec.rb
+46
-26
spec/support/kubernetes_helpers.rb
spec/support/kubernetes_helpers.rb
+27
-6
No files found.
app/models/project_services/kubernetes_service.rb
View file @
fc6e3515
...
@@ -116,30 +116,19 @@ class KubernetesService < DeploymentService
...
@@ -116,30 +116,19 @@ class KubernetesService < DeploymentService
# short time later
# short time later
def
terminals
(
environment
)
def
terminals
(
environment
)
with_reactive_cache
do
|
data
|
with_reactive_cache
do
|
data
|
pods
=
data
.
fetch
(
:pods
,
nil
)
pods
=
filter_by_label
(
data
[
:pods
],
app:
environment
.
slug
)
filter_pods
(
pods
,
app:
environment
.
slug
).
terminals
=
pods
.
flat_map
{
|
pod
|
terminals_for_pod
(
api_url
,
actual_namespace
,
pod
)
}
flat_map
{
|
pod
|
terminals_for_pod
(
api_url
,
actual_namespace
,
pod
)
}.
terminals
.
each
{
|
terminal
|
add_terminal_auth
(
terminal
,
terminal_auth
)
}
each
{
|
terminal
|
add_terminal_auth
(
terminal
,
terminal_auth
)
}
end
end
end
end
# Caches
all pod
s in the namespace so other calls don't need to block on
# Caches
resource
s in the namespace so other calls don't need to block on
# network access
.
# network access
def
calculate_reactive_cache
def
calculate_reactive_cache
return
unless
active?
&&
project
&&
!
project
.
pending_delete?
return
unless
active?
&&
project
&&
!
project
.
pending_delete?
kubeclient
=
build_kubeclient!
# Store as hashes, rather than as third-party types
pods
=
begin
kubeclient
.
get_pods
(
namespace:
actual_namespace
).
as_json
rescue
KubeException
=>
err
raise
err
unless
err
.
error_code
==
404
[]
end
# We may want to cache extra things in the future
# We may want to cache extra things in the future
{
pods:
pods
}
{
pods:
read_
pods
}
end
end
TEMPLATE_PLACEHOLDER
=
'Kubernetes namespace'
.
freeze
TEMPLATE_PLACEHOLDER
=
'Kubernetes namespace'
.
freeze
...
@@ -166,6 +155,16 @@ class KubernetesService < DeploymentService
...
@@ -166,6 +155,16 @@ class KubernetesService < DeploymentService
)
)
end
end
# Returns a hash of all pods in the namespace
def
read_pods
kubeclient
=
build_kubeclient!
kubeclient
.
get_pods
(
namespace:
actual_namespace
).
as_json
rescue
KubeException
=>
err
raise
err
unless
err
.
error_code
==
404
[]
end
def
kubeclient_ssl_options
def
kubeclient_ssl_options
opts
=
{
verify_ssl:
OpenSSL
::
SSL
::
VERIFY_PEER
}
opts
=
{
verify_ssl:
OpenSSL
::
SSL
::
VERIFY_PEER
}
...
@@ -181,11 +180,11 @@ class KubernetesService < DeploymentService
...
@@ -181,11 +180,11 @@ class KubernetesService < DeploymentService
{
bearer_token:
token
}
{
bearer_token:
token
}
end
end
def
join_api_url
(
*
parts
)
def
join_api_url
(
api_path
)
url
=
URI
.
parse
(
api_url
)
url
=
URI
.
parse
(
api_url
)
prefix
=
url
.
path
.
sub
(
%r{/+
\z
}
,
''
)
prefix
=
url
.
path
.
sub
(
%r{/+
\z
}
,
''
)
url
.
path
=
[
prefix
,
*
parts
].
join
(
"/"
)
url
.
path
=
[
prefix
,
api_path
].
join
(
"/"
)
url
.
to_s
url
.
to_s
end
end
...
...
lib/gitlab/kubernetes.rb
View file @
fc6e3515
...
@@ -8,13 +8,13 @@ module Gitlab
...
@@ -8,13 +8,13 @@ module Gitlab
)
)
# Filters an array of pods (as returned by the kubernetes API) by their labels
# Filters an array of pods (as returned by the kubernetes API) by their labels
def
filter_
pods
(
pod
s
,
labels
=
{})
def
filter_
by_label
(
item
s
,
labels
=
{})
pods
.
select
do
|
pod
|
items
.
select
do
|
item
|
metadata
=
pod
.
fetch
(
"metadata"
,
{})
metadata
=
item
.
fetch
(
"metadata"
,
{})
pod
_labels
=
metadata
.
fetch
(
"labels"
,
nil
)
item
_labels
=
metadata
.
fetch
(
"labels"
,
nil
)
next
unless
pod
_labels
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
end
end
...
...
spec/lib/gitlab/kubernetes_spec.rb
View file @
fc6e3515
require
'spec_helper'
require
'spec_helper'
describe
Gitlab
::
Kubernetes
do
describe
Gitlab
::
Kubernetes
do
include
KubernetesHelpers
include
described_class
include
described_class
describe
'#container_exec_url'
do
describe
'#container_exec_url'
do
...
@@ -36,4 +37,13 @@ describe Gitlab::Kubernetes do
...
@@ -36,4 +37,13 @@ describe Gitlab::Kubernetes do
it
{
expect
(
result
.
query
).
to
match
(
/\Acontainer=container\+1&/
)
}
it
{
expect
(
result
.
query
).
to
match
(
/\Acontainer=container\+1&/
)
}
end
end
end
end
describe
'#filter_by_label'
do
it
'returns matching labels'
do
matching_items
=
[
kube_pod
(
app:
'foo'
)]
items
=
matching_items
+
[
kube_pod
]
expect
(
filter_by_label
(
items
,
app:
'foo'
)).
to
eq
(
matching_items
)
end
end
end
end
spec/models/project_services/kubernetes_service_spec.rb
View file @
fc6e3515
...
@@ -7,24 +7,6 @@ describe KubernetesService, models: true, caching: true do
...
@@ -7,24 +7,6 @@ describe KubernetesService, models: true, caching: true do
let
(
:project
)
{
build_stubbed
(
:kubernetes_project
)
}
let
(
:project
)
{
build_stubbed
(
:kubernetes_project
)
}
let
(
:service
)
{
project
.
kubernetes_service
}
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
.
actual_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
describe
"Associations"
do
it
{
is_expected
.
to
belong_to
:project
}
it
{
is_expected
.
to
belong_to
:project
}
end
end
...
@@ -105,6 +87,34 @@ describe KubernetesService, models: true, caching: true do
...
@@ -105,6 +87,34 @@ describe KubernetesService, models: true, caching: true do
end
end
end
end
describe
'#actual_namespace'
do
subject
{
service
.
actual_namespace
}
it
"returns the default namespace"
do
is_expected
.
to
eq
(
service
.
send
(
:default_namespace
))
end
context
'when namespace is specified'
do
before
do
service
.
namespace
=
'my-namespace'
end
it
"returns the user-namespace"
do
is_expected
.
to
eq
(
'my-namespace'
)
end
end
context
'when service is not assigned to project'
do
before
do
service
.
project
=
nil
end
it
"does not return namespace"
do
is_expected
.
to
be_nil
end
end
end
describe
'#actual_namespace'
do
describe
'#actual_namespace'
do
subject
{
service
.
actual_namespace
}
subject
{
service
.
actual_namespace
}
...
@@ -134,6 +144,8 @@ describe KubernetesService, models: true, caching: true do
...
@@ -134,6 +144,8 @@ describe KubernetesService, models: true, caching: true do
end
end
describe
'#test'
do
describe
'#test'
do
let
(
:discovery_url
)
{
'https://kubernetes.example.com/api/v1'
}
before
do
before
do
stub_kubeclient_discover
stub_kubeclient_discover
end
end
...
@@ -142,7 +154,8 @@ describe KubernetesService, models: true, caching: true do
...
@@ -142,7 +154,8 @@ describe KubernetesService, models: true, caching: true do
let
(
:discovery_url
)
{
'https://kubernetes.example.com/prefix/api/v1'
}
let
(
:discovery_url
)
{
'https://kubernetes.example.com/prefix/api/v1'
}
it
'tests with the prefix'
do
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
(
service
.
test
[
:success
]).
to
be_truthy
expect
(
WebMock
).
to
have_requested
(
:get
,
discovery_url
).
once
expect
(
WebMock
).
to
have_requested
(
:get
,
discovery_url
).
once
...
@@ -170,9 +183,9 @@ describe KubernetesService, models: true, caching: true do
...
@@ -170,9 +183,9 @@ describe KubernetesService, models: true, caching: true do
end
end
context
'failure'
do
context
'failure'
do
let
(
:discovery_response
)
{
{
status:
404
}
}
it
'fails to read the discovery endpoint'
do
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
(
service
.
test
[
:success
]).
to
be_falsy
expect
(
WebMock
).
to
have_requested
(
:get
,
discovery_url
).
once
expect
(
WebMock
).
to
have_requested
(
:get
,
discovery_url
).
once
end
end
...
@@ -258,7 +271,6 @@ describe KubernetesService, models: true, caching: true do
...
@@ -258,7 +271,6 @@ describe KubernetesService, models: true, caching: true do
end
end
describe
'#calculate_reactive_cache'
do
describe
'#calculate_reactive_cache'
do
before
{
stub_kubeclient_pods
}
subject
{
service
.
calculate_reactive_cache
}
subject
{
service
.
calculate_reactive_cache
}
context
'when service is inactive'
do
context
'when service is inactive'
do
...
@@ -268,17 +280,25 @@ describe KubernetesService, models: true, caching: true do
...
@@ -268,17 +280,25 @@ describe KubernetesService, models: true, caching: true do
end
end
context
'when kubernetes responds with valid pods'
do
context
'when kubernetes responds with valid pods'
do
before
do
stub_kubeclient_pods
end
it
{
is_expected
.
to
eq
(
pods:
[
kube_pod
])
}
it
{
is_expected
.
to
eq
(
pods:
[
kube_pod
])
}
end
end
context
'when kubernetes responds with 500'
do
context
'when kubernetes responds with 500s'
do
let
(
:pods_response
)
{
{
status:
500
}
}
before
do
stub_kubeclient_pods
(
status:
500
)
end
it
{
expect
{
subject
}.
to
raise_error
(
KubeException
)
}
it
{
expect
{
subject
}.
to
raise_error
(
KubeException
)
}
end
end
context
'when kubernetes responds with 404'
do
context
'when kubernetes responds with 404s'
do
let
(
:pods_response
)
{
{
status:
404
}
}
before
do
stub_kubeclient_pods
(
status:
404
)
end
it
{
is_expected
.
to
eq
(
pods:
[])
}
it
{
is_expected
.
to
eq
(
pods:
[])
}
end
end
...
...
spec/support/kubernetes_helpers.rb
View file @
fc6e3515
module
KubernetesHelpers
module
KubernetesHelpers
include
Gitlab
::
Kubernetes
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
stub_kubeclient_discover
WebMock
.
stub_request
(
:get
,
service
.
api_url
+
'/api/v1'
).
to_return
(
kube_response
(
kube_v1_discovery_body
))
end
def
stub_kubeclient_pods
(
response
=
nil
)
stub_kubeclient_discover
pods_url
=
service
.
api_url
+
"/api/v1/namespaces/
#{
service
.
actual_namespace
}
/pods"
WebMock
.
stub_request
(
:get
,
pods_url
).
to_return
(
response
||
kube_pods_response
)
end
def
kube_v1_discovery_body
{
{
"kind"
=>
"APIResourceList"
,
"kind"
=>
"APIResourceList"
,
"resources"
=>
[
"resources"
=>
[
...
@@ -10,17 +29,19 @@ module KubernetesHelpers
...
@@ -10,17 +29,19 @@ module KubernetesHelpers
}
}
end
end
def
kube_pods_body
(
*
pods
)
def
kube_pods_body
{
"kind"
=>
"PodList"
,
{
"items"
=>
[
kube_pod
]
}
"kind"
=>
"PodList"
,
"items"
=>
[
kube_pod
]
}
end
end
# This is a partial response, it will have many more elements in reality but
# This is a partial response, it will have many more elements in reality but
# these are the ones we care about at the moment
# 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"
=>
{
"metadata"
=>
{
"name"
=>
"kube-pod"
,
"name"
=>
name
,
"creationTimestamp"
=>
"2016-11-25T19:55:19Z"
,
"creationTimestamp"
=>
"2016-11-25T19:55:19Z"
,
"labels"
=>
{
"app"
=>
app
}
"labels"
=>
{
"app"
=>
app
}
},
},
...
...
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