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
cd3c7e76
Commit
cd3c7e76
authored
Feb 13, 2020
by
Adrien Kohlbecker
Committed by
Nick Thomas
Feb 13, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Split PodLogsService per backend
parent
6b95c3f9
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
761 additions
and
765 deletions
+761
-765
app/models/concerns/reactive_caching.rb
app/models/concerns/reactive_caching.rb
+2
-0
ee/app/controllers/projects/logs_controller.rb
ee/app/controllers/projects/logs_controller.rb
+10
-6
ee/app/models/ee/clusters/platforms/kubernetes.rb
ee/app/models/ee/clusters/platforms/kubernetes.rb
+0
-137
ee/app/models/ee/environment.rb
ee/app/models/ee/environment.rb
+0
-14
ee/app/services/pod_logs/base_service.rb
ee/app/services/pod_logs/base_service.rb
+145
-0
ee/app/services/pod_logs/elasticsearch_service.rb
ee/app/services/pod_logs/elasticsearch_service.rb
+66
-0
ee/app/services/pod_logs/kubernetes_service.rb
ee/app/services/pod_logs/kubernetes_service.rb
+60
-0
ee/app/services/pod_logs_service.rb
ee/app/services/pod_logs_service.rb
+0
-118
ee/spec/controllers/projects/logs_controller_spec.rb
ee/spec/controllers/projects/logs_controller_spec.rb
+3
-3
ee/spec/features/projects/environments_pod_logs_spec.rb
ee/spec/features/projects/environments_pod_logs_spec.rb
+3
-12
ee/spec/models/ee/clusters/platforms/kubernetes_spec.rb
ee/spec/models/ee/clusters/platforms/kubernetes_spec.rb
+0
-208
ee/spec/models/environment_spec.rb
ee/spec/models/environment_spec.rb
+0
-25
ee/spec/services/pod_logs/base_service_spec.rb
ee/spec/services/pod_logs/base_service_spec.rb
+221
-0
ee/spec/services/pod_logs/elasticsearch_service_spec.rb
ee/spec/services/pod_logs/elasticsearch_service_spec.rb
+161
-0
ee/spec/services/pod_logs/kubernetes_service_spec.rb
ee/spec/services/pod_logs/kubernetes_service_spec.rb
+70
-0
ee/spec/services/pod_logs_service_spec.rb
ee/spec/services/pod_logs_service_spec.rb
+0
-237
lib/api/group_clusters.rb
lib/api/group_clusters.rb
+2
-2
lib/api/project_clusters.rb
lib/api/project_clusters.rb
+2
-2
locale/gitlab.pot
locale/gitlab.pot
+15
-0
spec/support/helpers/kubernetes_helpers.rb
spec/support/helpers/kubernetes_helpers.rb
+1
-1
No files found.
app/models/concerns/reactive_caching.rb
View file @
cd3c7e76
...
...
@@ -9,6 +9,8 @@ module ReactiveCaching
ExceededReactiveCacheLimit
=
Class
.
new
(
StandardError
)
included
do
extend
ActiveModel
::
Naming
class_attribute
:reactive_cache_key
class_attribute
:reactive_cache_lease_timeout
class_attribute
:reactive_cache_refresh_interval
...
...
ee/app/controllers/projects/logs_controller.rb
View file @
cd3c7e76
...
...
@@ -14,22 +14,22 @@ module Projects
end
def
k8s
render_logs
render_logs
(
::
PodLogs
::
KubernetesService
,
k8s_params
)
end
def
elasticsearch
render_logs
render_logs
(
::
PodLogs
::
ElasticsearchService
,
elasticsearch_params
)
end
private
def
render_logs
def
render_logs
(
service
,
permitted_params
)
::
Gitlab
::
UsageCounters
::
PodLogs
.
increment
(
project
.
id
)
::
Gitlab
::
PollingInterval
.
set_header
(
response
,
interval:
3_000
)
result
=
PodLogsService
.
new
(
environment
,
params:
filter
_params
).
execute
result
=
service
.
new
(
environment
,
params:
permitted
_params
).
execute
if
result
[
:status
]
==
:processing
if
result
.
nil?
head
:accepted
elsif
result
[
:status
]
==
:success
render
json:
result
...
...
@@ -42,7 +42,11 @@ module Projects
params
.
permit
(
:environment_name
)
end
def
filter_params
def
k8s_params
params
.
permit
(
:container_name
,
:pod_name
)
end
def
elasticsearch_params
params
.
permit
(
:container_name
,
:pod_name
,
:search
,
:start
,
:end
)
end
...
...
ee/app/models/ee/clusters/platforms/kubernetes.rb
View file @
cd3c7e76
...
...
@@ -6,11 +6,6 @@ module EE
module
Kubernetes
extend
ActiveSupport
::
Concern
extend
::
Gitlab
::
Utils
::
Override
include
::
Gitlab
::
Utils
::
StrongMemoize
CACHE_KEY_GET_POD_LOG
=
'get_pod_log'
LOGS_LIMIT
=
500
.
freeze
override
:calculate_reactive_cache_for
def
calculate_reactive_cache_for
(
environment
)
...
...
@@ -37,144 +32,12 @@ module EE
::
Gitlab
::
Kubernetes
::
RolloutStatus
.
from_deployments
(
*
deployments
,
pods:
pods
,
legacy_deployments:
legacy_deployments
)
end
def
read_pod_logs
(
environment_id
,
pod_name
,
namespace
,
container:
nil
,
search:
nil
,
start_time:
nil
,
end_time:
nil
)
# environment_id is required for use in reactive_cache_updated(),
# to invalidate the ETag cache.
with_reactive_cache
(
CACHE_KEY_GET_POD_LOG
,
'environment_id'
=>
environment_id
,
'pod_name'
=>
pod_name
,
'namespace'
=>
namespace
,
'container'
=>
container
,
'search'
=>
search
,
"start_time"
=>
start_time
,
"end_time"
=>
end_time
)
do
|
result
|
result
end
end
def
calculate_reactive_cache
(
request
,
opts
)
case
request
when
CACHE_KEY_GET_POD_LOG
container
=
opts
[
'container'
]
pod_name
=
opts
[
'pod_name'
]
namespace
=
opts
[
'namespace'
]
search
=
opts
[
'search'
]
start_time
=
opts
[
'start_time'
]
end_time
=
opts
[
'end_time'
]
handle_exceptions
(
_
(
'Pod not found'
),
pod_name:
pod_name
,
container_name:
container
,
search:
search
,
start_time:
start_time
,
end_time:
end_time
)
do
container
||=
container_names_of
(
pod_name
,
namespace
).
first
pod_logs
(
pod_name
,
namespace
,
container:
container
,
search:
search
,
start_time:
start_time
,
end_time:
end_time
)
end
end
end
def
reactive_cache_updated
(
request
,
opts
)
super
case
request
when
CACHE_KEY_GET_POD_LOG
environment
=
::
Environment
.
find_by
(
id:
opts
[
'environment_id'
])
return
unless
environment
method
=
elastic_stack_available?
?
:elasticsearch_project_logs_path
:
:k8s_project_logs_path
::
Gitlab
::
EtagCaching
::
Store
.
new
.
tap
do
|
store
|
store
.
touch
(
# not using send with untrusted input, this is better for readability
# rubocop:disable GitlabSecurity/PublicSend
::
Gitlab
::
Routing
.
url_helpers
.
send
(
method
,
environment
.
project
,
environment_name:
environment
.
name
,
pod_name:
opts
[
'pod_name'
],
container_name:
opts
[
'container_name'
],
format: :json
)
)
end
end
end
def
elastic_stack_available?
!!
cluster
.
application_elastic_stack
&
.
installed?
end
private
def
pod_logs
(
pod_name
,
namespace
,
container:
nil
,
search:
nil
,
start_time:
nil
,
end_time:
nil
)
logs
=
if
elastic_stack_available?
elastic_stack_pod_logs
(
namespace
,
pod_name
,
container
,
search
,
start_time
,
end_time
)
else
platform_pod_logs
(
namespace
,
pod_name
,
container
)
end
{
logs:
logs
,
status: :success
,
pod_name:
pod_name
,
container_name:
container
}
end
def
platform_pod_logs
(
namespace
,
pod_name
,
container_name
)
logs
=
kubeclient
.
get_pod_log
(
pod_name
,
namespace
,
container:
container_name
,
tail_lines:
LOGS_LIMIT
,
timestamps:
true
).
body
logs
.
strip
.
split
(
"
\n
"
).
map
do
|
line
|
# message contains a RFC3339Nano timestamp, then a space, then the log line.
# resolution of the nanoseconds can vary, so we split on the first space
values
=
line
.
split
(
' '
,
2
)
{
timestamp:
values
[
0
],
message:
values
[
1
]
}
end
end
def
elastic_stack_pod_logs
(
namespace
,
pod_name
,
container_name
,
search
,
start_time
,
end_time
)
client
=
elastic_stack_client
return
[]
if
client
.
nil?
::
Gitlab
::
Elasticsearch
::
Logs
.
new
(
client
).
pod_logs
(
namespace
,
pod_name
,
container_name
,
search
,
start_time
,
end_time
)
end
def
elastic_stack_client
strong_memoize
(
:elastic_stack_client
)
do
cluster
.
application_elastic_stack
&
.
elasticsearch_client
end
end
def
handle_exceptions
(
resource_not_found_error_message
,
opts
,
&
block
)
yield
rescue
Kubeclient
::
ResourceNotFoundError
{
error:
resource_not_found_error_message
,
status: :error
}.
merge
(
opts
)
rescue
Kubeclient
::
HttpError
=>
e
::
Gitlab
::
ErrorTracking
.
track_exception
(
e
)
{
error:
_
(
'Kubernetes API returned status code: %{error_code}'
)
%
{
error_code:
e
.
error_code
},
status: :error
}.
merge
(
opts
)
end
def
container_names_of
(
pod_name
,
namespace
)
return
[]
unless
pod_name
.
present?
pod_details
=
kubeclient
.
get_pod
(
pod_name
,
namespace
)
pod_details
.
spec
.
containers
.
collect
(
&
:name
)
end
def
read_deployments
(
namespace
)
kubeclient
.
get_deployments
(
namespace:
namespace
).
as_json
rescue
Kubeclient
::
ResourceNotFoundError
...
...
ee/app/models/ee/environment.rb
View file @
cd3c7e76
...
...
@@ -61,20 +61,6 @@ module EE
end
end
def
pod_names
return
[]
unless
rollout_status_available?
rollout_status
=
rollout_status_with_reactive_cache
# If cache has not been populated yet, rollout_status will be nil and the
# caller should try again later.
return
unless
rollout_status
rollout_status
.
instances
.
map
do
|
instance
|
instance
[
:pod_name
]
end
end
def
protected?
project
.
protected_environment_by_name
(
name
).
present?
end
...
...
ee/app/services/pod_logs/base_service.rb
0 → 100644
View file @
cd3c7e76
# frozen_string_literal: true
module
PodLogs
class
BaseService
<
::
BaseService
include
ReactiveCaching
include
Stepable
attr_reader
:environment
,
:params
CACHE_KEY_GET_POD_LOG
=
'get_pod_log'
K8S_NAME_MAX_LENGTH
=
253
SUCCESS_RETURN_KEYS
=
%i(status logs pod_name container_name pods)
.
freeze
def
id
environment
.
id
end
def
initialize
(
environment
,
params:
{})
@environment
=
environment
@params
=
filter_params
(
params
.
dup
.
stringify_keys
).
to_hash
end
def
execute
with_reactive_cache
(
CACHE_KEY_GET_POD_LOG
,
params
)
do
|
result
|
result
end
end
def
calculate_reactive_cache
(
request
,
_opts
)
case
request
when
CACHE_KEY_GET_POD_LOG
execute_steps
else
exception
=
StandardError
.
new
(
'Unknown reactive cache request'
)
Gitlab
::
ErrorTracking
.
track_and_raise_for_dev_exception
(
exception
,
request:
request
)
error
(
_
(
'Unknown cache key'
))
end
end
def
reactive_cache_updated
(
request
,
_opts
)
case
request
when
CACHE_KEY_GET_POD_LOG
::
Gitlab
::
EtagCaching
::
Store
.
new
.
tap
do
|
store
|
store
.
touch
(
etag_path
)
end
end
end
private
def
valid_params
%w(pod_name container_name)
end
def
check_param_lengths
(
_result
)
pod_name
=
params
[
'pod_name'
].
presence
container_name
=
params
[
'container_name'
].
presence
if
pod_name
&
.
length
.
to_i
>
K8S_NAME_MAX_LENGTH
return
error
(
_
(
'pod_name cannot be larger than %{max_length}'
\
' chars'
%
{
max_length:
K8S_NAME_MAX_LENGTH
}))
elsif
container_name
&
.
length
.
to_i
>
K8S_NAME_MAX_LENGTH
return
error
(
_
(
'container_name cannot be larger than'
\
' %{max_length} chars'
%
{
max_length:
K8S_NAME_MAX_LENGTH
}))
end
success
(
pod_name:
pod_name
,
container_name:
container_name
)
end
def
check_deployment_platform
(
result
)
unless
environment
.
deployment_platform
return
error
(
_
(
'No deployment platform available'
))
end
success
(
result
)
end
def
get_raw_pods
(
result
)
namespace
=
environment
.
deployment_namespace
result
[
:raw_pods
]
=
environment
.
deployment_platform
.
kubeclient
.
get_pods
(
namespace:
namespace
)
success
(
result
)
end
def
get_pod_names
(
result
)
result
[
:pods
]
=
result
[
:raw_pods
].
map
(
&
:metadata
).
map
(
&
:name
)
success
(
result
)
end
def
check_pod_name
(
result
)
# If pod_name is not received as parameter, get the pod logs of the first
# pod of this environment.
result
[
:pod_name
]
||=
result
[
:pods
].
first
unless
result
[
:pod_name
]
return
error
(
_
(
'No pods available'
))
end
unless
result
[
:pods
].
include?
(
result
[
:pod_name
])
return
error
(
_
(
'Pod does not exist'
))
end
success
(
result
)
end
def
check_container_name
(
result
)
pod_details
=
result
[
:raw_pods
].
first
{
|
p
|
p
.
metadata
.
name
==
result
[
:pod_name
]
}
containers
=
pod_details
.
spec
.
containers
.
map
(
&
:name
)
# select first container if not specified
result
[
:container_name
]
||=
containers
.
first
unless
result
[
:container_name
]
return
error
(
_
(
'No containers available'
))
end
unless
containers
.
include?
(
result
[
:container_name
])
return
error
(
_
(
'Container does not exist'
))
end
success
(
result
)
end
def
pod_logs
(
result
)
raise
NotImplementedError
end
def
etag_path
raise
NotImplementedError
end
def
filter_return_keys
(
result
)
result
.
slice
(
*
SUCCESS_RETURN_KEYS
)
end
def
filter_params
(
params
)
params
.
slice
(
*
valid_params
)
end
end
end
ee/app/services/pod_logs/elasticsearch_service.rb
0 → 100644
View file @
cd3c7e76
# frozen_string_literal: true
module
PodLogs
class
ElasticsearchService
<
BaseService
steps
:check_param_lengths
,
:check_deployment_platform
,
:get_raw_pods
,
:get_pod_names
,
:check_pod_name
,
:check_container_name
,
:check_times
,
:check_search
,
:pod_logs
,
:filter_return_keys
self
.
reactive_cache_worker_finder
=
->
(
id
,
_cache_key
,
params
)
{
new
(
Environment
.
find
(
id
),
params:
params
)
}
private
def
valid_params
%w(pod_name container_name search start end)
end
def
check_times
(
result
)
result
[
:start
]
=
params
[
'start'
]
if
params
.
key?
(
'start'
)
&&
Time
.
iso8601
(
params
[
'start'
])
result
[
:end
]
=
params
[
'end'
]
if
params
.
key?
(
'end'
)
&&
Time
.
iso8601
(
params
[
'end'
])
success
(
result
)
rescue
ArgumentError
error
(
_
(
'Invalid start or end time format'
))
end
def
check_search
(
result
)
result
[
:search
]
=
params
[
'search'
]
if
params
.
key?
(
'search'
)
success
(
result
)
end
def
pod_logs
(
result
)
namespace
=
environment
.
deployment_namespace
client
=
environment
.
deployment_platform
.
cluster
&
.
application_elastic_stack
&
.
elasticsearch_client
return
error
(
_
(
'Unable to connect to Elasticsearch'
))
unless
client
result
[
:logs
]
=
::
Gitlab
::
Elasticsearch
::
Logs
.
new
(
client
).
pod_logs
(
namespace
,
result
[
:pod_name
],
result
[
:container_name
],
result
[
:search
],
result
[
:start
],
result
[
:end
]
)
success
(
result
)
end
def
etag_path
::
Gitlab
::
Routing
.
url_helpers
.
elasticsearch_project_logs_path
(
environment
.
project
,
params
.
merge
({
environment_name:
environment
.
name
,
format: :json
})
)
end
end
end
ee/app/services/pod_logs/kubernetes_service.rb
0 → 100644
View file @
cd3c7e76
# frozen_string_literal: true
module
PodLogs
class
KubernetesService
<
BaseService
LOGS_LIMIT
=
500
.
freeze
steps
:check_param_lengths
,
:check_deployment_platform
,
:get_raw_pods
,
:get_pod_names
,
:check_pod_name
,
:check_container_name
,
:pod_logs
,
:filter_return_keys
self
.
reactive_cache_worker_finder
=
->
(
id
,
_cache_key
,
params
)
{
new
(
Environment
.
find
(
id
),
params:
params
)
}
private
def
pod_logs
(
result
)
logs
=
environment
.
deployment_platform
.
kubeclient
.
get_pod_log
(
result
[
:pod_name
],
environment
.
deployment_namespace
,
container:
result
[
:container_name
],
tail_lines:
LOGS_LIMIT
,
timestamps:
true
).
body
result
[
:logs
]
=
logs
.
strip
.
lines
(
chomp:
true
).
map
do
|
line
|
# message contains a RFC3339Nano timestamp, then a space, then the log line.
# resolution of the nanoseconds can vary, so we split on the first space
values
=
line
.
split
(
' '
,
2
)
{
timestamp:
values
[
0
],
message:
values
[
1
]
}
end
success
(
result
)
rescue
Kubeclient
::
ResourceNotFoundError
error
(
_
(
'Pod not found'
))
rescue
Kubeclient
::
HttpError
=>
e
::
Gitlab
::
ErrorTracking
.
track_exception
(
e
)
error
(
_
(
'Kubernetes API returned status code: %{error_code}'
)
%
{
error_code:
e
.
error_code
})
end
def
etag_path
::
Gitlab
::
Routing
.
url_helpers
.
k8s_project_logs_path
(
environment
.
project
,
params
.
merge
({
environment_name:
environment
.
name
,
format: :json
})
)
end
end
end
ee/app/services/pod_logs_service.rb
deleted
100644 → 0
View file @
6b95c3f9
# frozen_string_literal: true
class
PodLogsService
<
::
BaseService
include
Stepable
attr_reader
:environment
K8S_NAME_MAX_LENGTH
=
253
PARAMS
=
%w(pod_name container_name search start end)
.
freeze
SUCCESS_RETURN_KEYS
=
[
:status
,
:logs
,
:pod_name
,
:container_name
,
:pods
].
freeze
steps
:check_param_lengths
,
:check_deployment_platform
,
:check_pod_names
,
:check_pod_name
,
:check_times
,
:pod_logs
,
:filter_return_keys
def
initialize
(
environment
,
params:
{})
@environment
=
environment
@params
=
filter_params
(
params
.
dup
).
to_hash
end
def
execute
execute_steps
end
private
def
check_param_lengths
(
_result
)
pod_name
=
params
[
'pod_name'
].
presence
container_name
=
params
[
'container_name'
].
presence
if
pod_name
&
.
length
.
to_i
>
K8S_NAME_MAX_LENGTH
return
error
(
_
(
'pod_name cannot be larger than %{max_length}'
\
' chars'
%
{
max_length:
K8S_NAME_MAX_LENGTH
}))
elsif
container_name
&
.
length
.
to_i
>
K8S_NAME_MAX_LENGTH
return
error
(
_
(
'container_name cannot be larger than'
\
' %{max_length} chars'
%
{
max_length:
K8S_NAME_MAX_LENGTH
}))
end
success
(
pod_name:
pod_name
,
container_name:
container_name
)
end
def
check_deployment_platform
(
result
)
unless
environment
.
deployment_platform
return
error
(
_
(
'No deployment platform available'
))
end
success
(
result
)
end
def
check_pod_names
(
result
)
result
[
:pods
]
=
environment
.
pod_names
return
{
status: :processing
}
unless
result
[
:pods
]
success
(
result
)
end
def
check_pod_name
(
result
)
# If pod_name is not received as parameter, get the pod logs of the first
# pod of this environment.
result
[
:pod_name
]
||=
result
[
:pods
].
first
unless
result
[
:pod_name
]
return
error
(
_
(
'No pods available'
))
end
success
(
result
)
end
def
check_times
(
result
)
Time
.
iso8601
(
params
[
'start'
])
if
params
[
'start'
]
Time
.
iso8601
(
params
[
'end'
])
if
params
[
'end'
]
success
(
result
)
rescue
ArgumentError
error
(
_
(
'Invalid start or end time format'
))
end
def
pod_logs
(
result
)
response
=
environment
.
deployment_platform
.
read_pod_logs
(
environment
.
id
,
result
[
:pod_name
],
namespace
,
container:
result
[
:container_name
],
search:
params
[
'search'
],
start_time:
params
[
'start'
],
end_time:
params
[
'end'
]
)
return
{
status: :processing
}
unless
response
result
.
merge!
(
response
.
slice
(
:pod_name
,
:container_name
,
:logs
))
if
response
[
:status
]
==
:error
error
(
response
[
:error
]).
reverse_merge
(
result
)
else
success
(
result
)
end
end
def
filter_return_keys
(
result
)
result
.
slice
(
*
SUCCESS_RETURN_KEYS
)
end
def
filter_params
(
params
)
params
.
slice
(
*
PARAMS
)
end
def
namespace
environment
.
deployment_namespace
end
end
ee/spec/controllers/projects/logs_controller_spec.rb
View file @
cd3c7e76
...
...
@@ -73,7 +73,7 @@ describe Projects::LogsController do
before
do
stub_licensed_features
(
pod_logs:
true
)
allow_any_instance_of
(
PodLog
sService
).
to
receive
(
:execute
).
and_return
(
service_result
)
allow_any_instance_of
(
::
PodLogs
::
Kubernete
sService
).
to
receive
(
:execute
).
and_return
(
service_result
)
end
shared_examples
'resource not found'
do
|
message
|
...
...
@@ -159,8 +159,8 @@ describe Projects::LogsController do
end
end
context
'when service
returns status processing
'
do
let
(
:service_result
)
{
{
status: :processing
}
}
context
'when service
is processing (returns nil)
'
do
let
(
:service_result
)
{
nil
}
it
'renders accepted'
do
get
:k8s
,
params:
environment_params
(
pod_name:
pod_name
,
format: :json
)
...
...
ee/spec/features/projects/environments_pod_logs_spec.rb
View file @
cd3c7e76
...
...
@@ -7,7 +7,7 @@ describe 'Environment > Pod Logs', :js do
SCROLL_DISTANCE
=
400
let
(
:pod_names
)
{
%w(
foo bar
)
}
let
(
:pod_names
)
{
%w(
kube-pod
)
}
let
(
:pod_name
)
{
pod_names
.
first
}
let
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:environment
)
{
create
(
:environment
,
project:
project
)
}
...
...
@@ -19,18 +19,9 @@ describe 'Environment > Pod Logs', :js do
create
(
:cluster
,
:provided_by_gcp
,
environment_scope:
'*'
,
projects:
[
project
])
create
(
:deployment
,
:success
,
environment:
environment
)
stub_kubeclient_pod
_details
(
pod_name
,
environment
.
deployment_namespace
)
stub_kubeclient_pod
s
(
environment
.
deployment_namespace
)
stub_kubeclient_logs
(
pod_name
,
environment
.
deployment_namespace
,
container:
'container-0'
)
# rollout_status_instances = [{ pod_name: foo }, {pod_name: bar}]
rollout_status_instances
=
pod_names
.
collect
{
|
name
|
{
pod_name:
name
}
}
rollout_status
=
instance_double
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
,
instances:
rollout_status_instances
)
allow_any_instance_of
(
EE
::
Environment
).
to
receive
(
:rollout_status_with_reactive_cache
)
.
and_return
(
rollout_status
)
sign_in
(
project
.
owner
)
end
...
...
@@ -64,7 +55,7 @@ describe 'Environment > Pod Logs', :js do
find
(
".dropdown-menu-toggle:not([disabled])"
).
click
dropdown_items
=
find
(
".dropdown-menu"
).
all
(
".dropdown-item"
)
expect
(
dropdown_items
.
size
).
to
eq
(
2
)
expect
(
dropdown_items
.
size
).
to
eq
(
1
)
dropdown_items
.
each_with_index
do
|
item
,
i
|
expect
(
item
.
text
).
to
eq
(
pod_names
[
i
])
...
...
ee/spec/models/ee/clusters/platforms/kubernetes_spec.rb
View file @
cd3c7e76
...
...
@@ -134,214 +134,6 @@ describe Clusters::Platforms::Kubernetes do
end
end
describe
'#read_pod_logs'
do
let
(
:environment
)
{
create
(
:environment
)
}
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
let
(
:pod_name
)
{
'pod-1'
}
let
(
:namespace
)
{
'app'
}
let
(
:container
)
{
'some-container'
}
let
(
:expected_logs
)
do
[
{
message:
"Log 1"
,
timestamp:
"2019-12-13T14:04:22.123456Z"
},
{
message:
"Log 2"
,
timestamp:
"2019-12-13T14:04:23.123456Z"
},
{
message:
"Log 3"
,
timestamp:
"2019-12-13T14:04:24.123456Z"
}
]
end
subject
{
service
.
read_pod_logs
(
environment
.
id
,
pod_name
,
namespace
,
container:
container
)
}
shared_examples
'successful log request'
do
it
do
expect
(
subject
[
:logs
]).
to
eq
(
expected_logs
)
expect
(
subject
[
:status
]).
to
eq
(
:success
)
expect
(
subject
[
:pod_name
]).
to
eq
(
pod_name
)
expect
(
subject
[
:container_name
]).
to
eq
(
container
)
end
end
shared_examples
'returns pod_name and container_name'
do
it
do
expect
(
subject
[
:pod_name
]).
to
eq
(
pod_name
)
expect
(
subject
[
:container_name
]).
to
eq
(
container
)
end
end
context
'with reactive cache'
do
before
do
synchronous_reactive_cache
(
service
)
end
context
'when ElasticSearch is enabled'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
let!
(
:elastic_stack
)
{
create
(
:clusters_applications_elastic_stack
,
:installed
,
cluster:
cluster
)
}
before
do
expect_any_instance_of
(
::
Clusters
::
Applications
::
ElasticStack
).
to
receive
(
:elasticsearch_client
).
at_least
(
:once
).
and_return
(
Elasticsearch
::
Transport
::
Client
.
new
)
expect_any_instance_of
(
::
Gitlab
::
Elasticsearch
::
Logs
).
to
receive
(
:pod_logs
).
and_return
(
expected_logs
)
end
include_examples
'successful log request'
end
context
'when kubernetes responds with valid logs'
do
before
do
stub_kubeclient_logs
(
pod_name
,
namespace
,
container:
container
)
end
context
'on a project level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
include_examples
'successful log request'
end
context
'on a group level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:group
,
platform_kubernetes:
service
)
}
include_examples
'successful log request'
end
context
'on an instance level cluster'
do
let
(
:cluster
)
{
create
(
:cluster
,
:instance
,
platform_kubernetes:
service
)
}
include_examples
'successful log request'
end
end
context
'when kubernetes responds with 500s'
do
before
do
stub_kubeclient_logs
(
pod_name
,
namespace
,
container:
'some-container'
,
status:
500
)
end
it_behaves_like
'kubernetes API error'
,
500
it_behaves_like
'returns pod_name and container_name'
end
context
'when container does not exist'
do
before
do
container
=
'some-container'
stub_kubeclient_logs
(
pod_name
,
namespace
,
container:
container
,
status:
400
,
message:
"container
#{
container
}
is not valid for pod
#{
pod_name
}
"
)
end
it_behaves_like
'kubernetes API error'
,
400
it_behaves_like
'returns pod_name and container_name'
end
context
'when kubernetes responds with 404s'
do
before
do
stub_kubeclient_logs
(
pod_name
,
namespace
,
container:
'some-container'
,
status:
404
)
end
it_behaves_like
'resource not found error'
,
'Pod not found'
it_behaves_like
'returns pod_name and container_name'
end
context
'when container name is not specified'
do
let
(
:container
)
{
'container-0'
}
subject
{
service
.
read_pod_logs
(
environment
.
id
,
pod_name
,
namespace
)
}
before
do
stub_kubeclient_pod_details
(
pod_name
,
namespace
)
stub_kubeclient_logs
(
pod_name
,
namespace
,
container:
container
)
end
include_examples
'successful log request'
end
end
context
'with caching'
,
:use_clean_rails_memory_store_caching
do
let
(
:opts
)
do
[
'get_pod_log'
,
{
'environment_id'
=>
environment
.
id
,
'pod_name'
=>
pod_name
,
'namespace'
=>
namespace
,
'container'
=>
container
,
'search'
=>
nil
,
'start_time'
=>
nil
,
'end_time'
=>
nil
}
]
end
context
'result is cacheable'
do
before
do
stub_kubeclient_logs
(
pod_name
,
namespace
,
container:
container
)
end
it
do
result
=
subject
expect
{
stub_reactive_cache
(
service
,
result
,
opts
)
}.
not_to
raise_error
end
end
context
'when value present in cache'
do
let
(
:return_value
)
{
{
'status'
=>
:success
,
'logs'
=>
'logs'
}
}
before
do
stub_reactive_cache
(
service
,
return_value
,
opts
)
end
it
'returns cached value'
do
result
=
subject
expect
(
result
).
to
eq
(
return_value
)
end
end
context
'when value not present in cache'
do
it
'returns nil'
do
expect
(
ReactiveCachingWorker
)
.
to
receive
(
:perform_async
)
.
with
(
service
.
class
,
service
.
id
,
*
opts
)
result
=
subject
expect
(
result
).
to
eq
(
nil
)
end
end
end
describe
'#reactive_cache_updated'
do
let
(
:opts
)
do
{
'environment_id'
=>
environment
.
id
,
'pod_name'
=>
pod_name
,
'namespace'
=>
namespace
,
'container'
=>
container
}
end
subject
{
service
.
reactive_cache_updated
(
'get_pod_log'
,
opts
)
}
it
'expires k8s_pod_logs etag cache'
do
expect_next_instance_of
(
Gitlab
::
EtagCaching
::
Store
)
do
|
store
|
expect
(
store
).
to
receive
(
:touch
)
.
with
(
::
Gitlab
::
Routing
.
url_helpers
.
k8s_project_logs_path
(
environment
.
project
,
environment_name:
environment
.
name
,
pod_name:
opts
[
'pod_name'
],
container_name:
opts
[
'container_name'
],
format: :json
)
)
.
and_call_original
end
subject
end
end
end
describe
'#calculate_reactive_cache_for'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
platform_kubernetes:
service
)
}
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
...
...
ee/spec/models/environment_spec.rb
View file @
cd3c7e76
...
...
@@ -57,31 +57,6 @@ describe Environment, :use_clean_rails_memory_store_caching do
end
end
describe
'#pod_names'
do
context
'when environment does not have a rollout status'
do
it
'returns an empty array'
do
expect
(
environment
.
pod_names
).
to
eq
([])
end
end
context
'when environment has a rollout status'
do
let
(
:pod_name
)
{
'pod_1'
}
let
(
:rollout_status
)
{
instance_double
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
,
instances:
[{
pod_name:
pod_name
}])
}
before
do
create
(
:cluster
,
:provided_by_gcp
,
environment_scope:
'*'
,
projects:
[
project
])
create
(
:deployment
,
:success
,
environment:
environment
)
end
it
'returns the pod_names'
do
allow
(
environment
).
to
receive
(
:rollout_status_with_reactive_cache
)
.
and_return
(
rollout_status
)
expect
(
environment
.
pod_names
).
to
eq
([
pod_name
])
end
end
end
describe
'#protected?'
do
subject
{
environment
.
protected?
}
...
...
ee/spec/services/pod_logs/base_service_spec.rb
0 → 100644
View file @
cd3c7e76
# frozen_string_literal: true
require
'spec_helper'
describe
::
PodLogs
::
BaseService
do
include
KubernetesHelpers
let_it_be
(
:environment
,
refind:
true
)
{
create
(
:environment
)
}
let
(
:pod_name
)
{
'pod-1'
}
let
(
:container_name
)
{
'container-0'
}
let
(
:params
)
{
{}
}
let
(
:raw_pods
)
do
JSON
.
parse
([
kube_pod
(
name:
pod_name
)
].
to_json
,
object_class:
OpenStruct
)
end
subject
{
described_class
.
new
(
environment
,
params:
params
)
}
describe
'#initialize'
do
let
(
:params
)
do
{
'container_name'
=>
container_name
,
'another_param'
=>
'foo'
}
end
it
'filters the parameters'
do
expect
(
subject
.
environment
).
to
eq
(
environment
)
expect
(
subject
.
params
).
to
eq
({
'container_name'
=>
container_name
})
expect
(
subject
.
params
.
equal?
(
params
)).
to
be
(
false
)
end
end
describe
'#check_param_lengths'
do
context
'when pod_name and container_name are provided'
do
let
(
:params
)
do
{
'pod_name'
=>
pod_name
,
'container_name'
=>
container_name
}
end
it
'returns success'
do
result
=
subject
.
send
(
:check_param_lengths
,
{})
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:pod_name
]).
to
eq
(
pod_name
)
expect
(
result
[
:container_name
]).
to
eq
(
container_name
)
end
end
context
'when pod_name is too long'
do
let
(
:params
)
do
{
'pod_name'
=>
"a very long string."
*
15
}
end
it
'returns an error'
do
result
=
subject
.
send
(
:check_param_lengths
,
{})
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'pod_name cannot be larger than 253 chars'
)
end
end
context
'when container_name is too long'
do
let
(
:params
)
do
{
'container_name'
=>
"a very long string."
*
15
}
end
it
'returns an error'
do
result
=
subject
.
send
(
:check_param_lengths
,
{})
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'container_name cannot be larger than 253 chars'
)
end
end
end
describe
'#check_deployment_platform'
do
it
'returns success when deployment platform exist'
do
create
(
:cluster
,
:provided_by_gcp
,
environment_scope:
'*'
,
projects:
[
environment
.
project
])
create
(
:deployment
,
:success
,
environment:
environment
)
result
=
subject
.
send
(
:check_deployment_platform
,
{})
expect
(
result
[
:status
]).
to
eq
(
:success
)
end
it
'returns error when deployment platform does not exist'
do
result
=
subject
.
send
(
:check_deployment_platform
,
{})
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'No deployment platform available'
)
end
end
describe
'#get_raw_pods'
do
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
it
'returns success with passthrough k8s response'
do
create
(
:cluster
,
:provided_by_gcp
,
environment_scope:
'*'
,
projects:
[
environment
.
project
])
create
(
:deployment
,
:success
,
environment:
environment
)
stub_kubeclient_pods
(
environment
.
deployment_namespace
)
result
=
subject
.
send
(
:get_raw_pods
,
{})
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:raw_pods
].
first
).
to
be_a
(
Kubeclient
::
Resource
)
end
end
describe
'#get_pod_names'
do
it
'returns success with a list of pods'
do
result
=
subject
.
send
(
:get_pod_names
,
raw_pods:
raw_pods
)
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:pods
]).
to
eq
([
pod_name
])
end
end
describe
'#check_pod_name'
do
it
'returns success if pod_name was specified'
do
result
=
subject
.
send
(
:check_pod_name
,
pod_name:
pod_name
,
pods:
[
pod_name
])
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:pod_name
]).
to
eq
(
pod_name
)
end
it
'returns success if pod_name was not specified but there are pods'
do
result
=
subject
.
send
(
:check_pod_name
,
pod_name:
nil
,
pods:
[
pod_name
])
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:pod_name
]).
to
eq
(
pod_name
)
end
it
'returns error if pod_name was not specified and there are no pods'
do
result
=
subject
.
send
(
:check_pod_name
,
pod_name:
nil
,
pods:
[])
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'No pods available'
)
end
it
'returns error if pod_name was specified but does not exist'
do
result
=
subject
.
send
(
:check_pod_name
,
pod_name:
'another_pod'
,
pods:
[
pod_name
])
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'Pod does not exist'
)
end
end
describe
'#check_container_name'
do
it
'returns success if container_name was specified'
do
result
=
subject
.
send
(
:check_container_name
,
container_name:
container_name
,
pod_name:
pod_name
,
raw_pods:
raw_pods
)
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:container_name
]).
to
eq
(
container_name
)
end
it
'returns success if container_name was not specified and there are containers'
do
result
=
subject
.
send
(
:check_container_name
,
pod_name:
pod_name
,
raw_pods:
raw_pods
)
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:container_name
]).
to
eq
(
container_name
)
end
it
'returns error if container_name was not specified and there are no containers on the pod'
do
raw_pods
.
first
.
spec
.
containers
=
[]
result
=
subject
.
send
(
:check_container_name
,
pod_name:
pod_name
,
raw_pods:
raw_pods
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'No containers available'
)
end
it
'returns error if container_name was specified but does not exist'
do
result
=
subject
.
send
(
:check_container_name
,
container_name:
'foo'
,
pod_name:
pod_name
,
raw_pods:
raw_pods
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'Container does not exist'
)
end
end
describe
'#reactive_cache_updated'
do
context
'get_pod_log'
do
let
(
:cache_key
)
{
'get_pod_log'
}
it
'expires k8s_pod_logs etag cache'
do
expected_path
=
"/root/autodevops-deploy/-/logs/k8s.json"
allow
(
subject
).
to
receive
(
:etag_path
).
and_return
(
expected_path
)
allow_next_instance_of
(
Gitlab
::
EtagCaching
::
Store
)
do
|
store
|
allow
(
store
).
to
receive
(
:touch
).
with
(
expected_path
).
and_call_original
end
subject
.
reactive_cache_updated
(
cache_key
,
{})
end
end
end
end
ee/spec/services/pod_logs/elasticsearch_service_spec.rb
0 → 100644
View file @
cd3c7e76
# frozen_string_literal: true
require
'spec_helper'
describe
::
PodLogs
::
ElasticsearchService
do
let_it_be
(
:environment
,
refind:
true
)
{
create
(
:environment
)
}
let
(
:pod_name
)
{
'pod-1'
}
let
(
:container_name
)
{
'container-1'
}
let
(
:search
)
{
'foo -bar'
}
let
(
:start_time
)
{
'2019-01-02T12:13:14+02:00'
}
let
(
:end_time
)
{
'2019-01-03T12:13:14+02:00'
}
let
(
:params
)
{
{}
}
let
(
:expected_logs
)
do
[
{
message:
"Log 1"
,
timestamp:
"2019-12-13T14:04:22.123456Z"
},
{
message:
"Log 2"
,
timestamp:
"2019-12-13T14:04:23.123456Z"
},
{
message:
"Log 3"
,
timestamp:
"2019-12-13T14:04:24.123456Z"
}
]
end
subject
{
described_class
.
new
(
environment
,
params:
params
)
}
describe
'#check_times'
do
context
'with start and end provided and valid'
do
let
(
:params
)
do
{
'start'
=>
start_time
,
'end'
=>
end_time
}
end
it
'returns success with times'
do
result
=
subject
.
send
(
:check_times
,
{})
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:start
]).
to
eq
(
start_time
)
expect
(
result
[
:end
]).
to
eq
(
end_time
)
end
end
context
'with start and end not provided'
do
let
(
:params
)
do
{}
end
it
'returns success with nothing else'
do
result
=
subject
.
send
(
:check_times
,
{})
expect
(
result
.
keys
.
length
).
to
eq
(
1
)
expect
(
result
[
:status
]).
to
eq
(
:success
)
end
end
context
'with start valid and end invalid'
do
let
(
:params
)
do
{
'start'
=>
start_time
,
'end'
=>
'invalid date'
}
end
it
'returns error'
do
result
=
subject
.
send
(
:check_times
,
{})
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'Invalid start or end time format'
)
end
end
context
'with start invalid and end valid'
do
let
(
:params
)
do
{
'start'
=>
'invalid date'
,
'end'
=>
end_time
}
end
it
'returns error'
do
result
=
subject
.
send
(
:check_times
,
{})
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'Invalid start or end time format'
)
end
end
end
describe
'#check_search'
do
context
'with search provided and valid'
do
let
(
:params
)
do
{
'search'
=>
search
}
end
it
'returns success with search'
do
result
=
subject
.
send
(
:check_search
,
{})
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:search
]).
to
eq
(
search
)
end
end
context
'with search not provided'
do
let
(
:params
)
do
{}
end
it
'returns success with nothing else'
do
result
=
subject
.
send
(
:check_search
,
{})
expect
(
result
.
keys
.
length
).
to
eq
(
1
)
expect
(
result
[
:status
]).
to
eq
(
:success
)
end
end
end
describe
'#pod_logs'
do
let
(
:cluster
)
{
create
(
:cluster
,
:provided_by_gcp
,
environment_scope:
'*'
,
projects:
[
environment
.
project
])
}
let
(
:result_arg
)
do
{
pod_name:
pod_name
,
container_name:
container_name
,
search:
search
,
start:
start_time
,
end:
end_time
}
end
before
do
create
(
:deployment
,
:success
,
environment:
environment
)
create
(
:clusters_applications_elastic_stack
,
:installed
,
cluster:
cluster
)
end
it
'returns the logs'
do
allow_any_instance_of
(
::
Clusters
::
Applications
::
ElasticStack
)
.
to
receive
(
:elasticsearch_client
)
.
and_return
(
Elasticsearch
::
Transport
::
Client
.
new
)
allow_any_instance_of
(
::
Gitlab
::
Elasticsearch
::
Logs
)
.
to
receive
(
:pod_logs
)
.
with
(
environment
.
deployment_namespace
,
pod_name
,
container_name
,
search
,
start_time
,
end_time
)
.
and_return
(
expected_logs
)
result
=
subject
.
send
(
:pod_logs
,
result_arg
)
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:logs
]).
to
eq
(
expected_logs
)
end
it
'returns an error when ES is unreachable'
do
allow_any_instance_of
(
::
Clusters
::
Applications
::
ElasticStack
)
.
to
receive
(
:elasticsearch_client
)
.
and_return
(
nil
)
result
=
subject
.
send
(
:pod_logs
,
result_arg
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'Unable to connect to Elasticsearch'
)
end
end
end
ee/spec/services/pod_logs/kubernetes_service_spec.rb
0 → 100644
View file @
cd3c7e76
# frozen_string_literal: true
require
'spec_helper'
describe
::
PodLogs
::
KubernetesService
do
include
KubernetesHelpers
let_it_be
(
:environment
,
refind:
true
)
{
create
(
:environment
)
}
let
(
:pod_name
)
{
'pod-1'
}
let
(
:container_name
)
{
'container-1'
}
let
(
:params
)
{
{}
}
let
(
:expected_logs
)
do
[
{
message:
"Log 1"
,
timestamp:
"2019-12-13T14:04:22.123456Z"
},
{
message:
"Log 2"
,
timestamp:
"2019-12-13T14:04:23.123456Z"
},
{
message:
"Log 3"
,
timestamp:
"2019-12-13T14:04:24.123456Z"
}
]
end
subject
{
described_class
.
new
(
environment
,
params:
params
)
}
describe
'#pod_logs'
do
let
(
:result_arg
)
do
{
pod_name:
pod_name
,
container_name:
container_name
}
end
let
(
:service
)
{
create
(
:cluster_platform_kubernetes
,
:configured
)
}
before
do
create
(
:cluster
,
:provided_by_gcp
,
environment_scope:
'*'
,
projects:
[
environment
.
project
])
create
(
:deployment
,
:success
,
environment:
environment
)
end
it
'returns the logs'
do
stub_kubeclient_logs
(
pod_name
,
environment
.
deployment_namespace
,
container:
container_name
)
result
=
subject
.
send
(
:pod_logs
,
result_arg
)
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:logs
]).
to
eq
(
expected_logs
)
end
it
'handles Not Found errors from k8s'
do
allow_any_instance_of
(
Gitlab
::
Kubernetes
::
KubeClient
)
.
to
receive
(
:get_pod_log
)
.
with
(
any_args
)
.
and_raise
(
Kubeclient
::
ResourceNotFoundError
.
new
(
404
,
'Not Found'
,
{}))
result
=
subject
.
send
(
:pod_logs
,
result_arg
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'Pod not found'
)
end
it
'handles HTTP errors from k8s'
do
allow_any_instance_of
(
Gitlab
::
Kubernetes
::
KubeClient
)
.
to
receive
(
:get_pod_log
)
.
with
(
any_args
)
.
and_raise
(
Kubeclient
::
HttpError
.
new
(
500
,
'Error'
,
{}))
result
=
subject
.
send
(
:pod_logs
,
result_arg
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'Kubernetes API returned status code: 500'
)
end
end
end
ee/spec/services/pod_logs_service_spec.rb
deleted
100644 → 0
View file @
6b95c3f9
# frozen_string_literal: true
require
'spec_helper'
describe
PodLogsService
do
include
KubernetesHelpers
include
ReactiveCachingHelpers
describe
'#execute'
do
let
(
:environment
)
{
create
(
:environment
,
name:
'production'
)
}
let
(
:project
)
{
environment
.
project
}
let
(
:pod_name
)
{
'pod-1'
}
let
(
:response_pod_name
)
{
pod_name
}
let
(
:pods
)
{
[
pod_name
]
}
let
(
:container_name
)
{
'container-1'
}
let
(
:search
)
{
nil
}
let
(
:logs
)
{
[
'Log 1'
,
'Log 2'
,
'Log 3'
]
}
let
(
:result
)
{
subject
.
execute
}
let
(
:start_time
)
{
nil
}
let
(
:end_time
)
{
nil
}
let
(
:params
)
do
ActionController
::
Parameters
.
new
(
{
'pod_name'
=>
pod_name
,
'container_name'
=>
container_name
,
'search'
=>
search
,
'start'
=>
start_time
,
'end'
=>
end_time
}
).
permit!
end
subject
{
described_class
.
new
(
environment
,
params:
params
)
}
shared_examples
'success'
do
|
message
|
it
do
expect
(
result
[
:status
]).
to
eq
(
:success
)
expect
(
result
[
:logs
]).
to
eq
(
logs
)
expect
(
result
[
:pods
]).
to
eq
(
pods
)
expect
(
result
[
:pod_name
]).
to
eq
(
response_pod_name
)
expect
(
result
[
:container_name
]).
to
eq
(
container_name
)
end
end
shared_examples
'error'
do
|
message
|
it
do
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
message
)
end
end
shared_examples
'returns pod_name and container_name'
do
it
do
expect
(
result
[
:pod_name
]).
to
eq
(
response_pod_name
)
expect
(
result
[
:container_name
]).
to
eq
(
container_name
)
end
end
shared_context
'return error'
do
|
message
|
before
do
allow_any_instance_of
(
EE
::
Clusters
::
Platforms
::
Kubernetes
).
to
receive
(
:read_pod_logs
)
.
with
(
environment
.
id
,
pod_name
,
environment
.
deployment_namespace
,
container:
container_name
,
search:
search
,
start_time:
start_time
,
end_time:
end_time
)
.
and_return
({
status: :error
,
error:
message
,
pod_name:
response_pod_name
,
container_name:
container_name
})
end
end
shared_context
'return success'
do
before
do
allow_any_instance_of
(
EE
::
Clusters
::
Platforms
::
Kubernetes
).
to
receive
(
:read_pod_logs
)
.
with
(
environment
.
id
,
response_pod_name
,
environment
.
deployment_namespace
,
container:
container_name
,
search:
search
,
start_time:
start_time
,
end_time:
end_time
)
.
and_return
({
status: :success
,
logs:
[
"Log 1"
,
"Log 2"
,
"Log 3"
],
pod_name:
response_pod_name
,
container_name:
container_name
})
end
end
context
'when pod name is too large'
do
let
(
:pod_name
)
{
'1'
*
254
}
it_behaves_like
'error'
,
'pod_name cannot be larger than 253 chars'
end
context
'when container name is too large'
do
let
(
:container_name
)
{
'1'
*
254
}
it_behaves_like
'error'
,
'container_name cannot be larger than 253 chars'
end
context
'without deployment platform'
do
it_behaves_like
'error'
,
'No deployment platform available'
end
context
'with deployment platform'
do
let
(
:rollout_status
)
do
instance_double
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
,
instances:
[{
pod_name:
response_pod_name
}])
end
before
do
create
(
:cluster
,
:provided_by_gcp
,
environment_scope:
'*'
,
projects:
[
project
])
create
(
:deployment
,
:success
,
environment:
environment
)
allow
(
environment
).
to
receive
(
:rollout_status_with_reactive_cache
)
.
and_return
(
rollout_status
)
end
context
'when pod does not exist'
do
include_context
'return error'
,
'Pod not found'
it_behaves_like
'error'
,
'Pod not found'
it_behaves_like
'returns pod_name and container_name'
end
context
'when container_name is specified'
do
include_context
'return success'
it_behaves_like
'success'
end
context
'when container_name is not specified'
do
let
(
:container_name
)
{
nil
}
let
(
:params
)
do
ActionController
::
Parameters
.
new
(
{
'pod_name'
=>
pod_name
,
'container_name'
=>
nil
}
).
permit!
end
include_context
'return success'
it_behaves_like
'success'
end
context
'when pod_name is not specified'
do
let
(
:pod_name
)
{
''
}
let
(
:container_name
)
{
nil
}
let
(
:first_pod_name
)
{
'some-pod'
}
let
(
:pods
)
{
[
first_pod_name
]
}
let
(
:response_pod_name
)
{
first_pod_name
}
include_context
'return success'
it_behaves_like
'success'
it
'returns logs of first pod'
do
expect_any_instance_of
(
EE
::
Clusters
::
Platforms
::
Kubernetes
).
to
receive
(
:read_pod_logs
)
.
with
(
environment
.
id
,
first_pod_name
,
environment
.
deployment_namespace
,
container:
nil
,
search:
search
,
start_time:
start_time
,
end_time:
end_time
)
subject
.
execute
end
context
'when there are no pods'
do
let
(
:rollout_status
)
{
instance_double
(
::
Gitlab
::
Kubernetes
::
RolloutStatus
,
instances:
[])
}
it
'returns error'
do
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:message
]).
to
eq
(
'No pods available'
)
end
end
context
'when rollout_status cache is empty'
do
before
do
allow
(
environment
).
to
receive
(
:rollout_status_with_reactive_cache
)
.
and_return
(
nil
)
end
it
'returns nil'
do
expect
(
subject
.
execute
).
to
eq
(
status: :processing
,
last_step: :check_pod_names
)
end
end
end
context
'when search is specified'
do
let
(
:pod_name
)
{
'some-pod'
}
let
(
:container_name
)
{
nil
}
let
(
:search
)
{
'foo +bar'
}
include_context
'return success'
it_behaves_like
'success'
end
context
'when start and end time is specified'
do
let
(
:pod_name
)
{
'some-pod'
}
let
(
:container_name
)
{
nil
}
let
(
:start_time
)
{
'2019-12-13T14:35:34.034Z'
}
let
(
:end_time
)
{
'2019-12-13T14:35:34.034Z'
}
include_context
'return success'
it_behaves_like
'success'
end
context
'when start and end time are invalid'
do
let
(
:pod_name
)
{
'some-pod'
}
let
(
:container_name
)
{
nil
}
let
(
:start_time
)
{
'1'
}
let
(
:end_time
)
{
'2'
}
it_behaves_like
'error'
,
'Invalid start or end time format'
end
context
'when error is returned'
do
include_context
'return error'
,
'Kubernetes API returned status code: 400'
it_behaves_like
'error'
,
'Kubernetes API returned status code: 400'
it_behaves_like
'returns pod_name and container_name'
end
context
'when nil is returned'
do
before
do
allow_any_instance_of
(
EE
::
Clusters
::
Platforms
::
Kubernetes
).
to
receive
(
:read_pod_logs
)
.
with
(
environment
.
id
,
pod_name
,
environment
.
deployment_namespace
,
container:
container_name
,
search:
search
,
start_time:
start_time
,
end_time:
end_time
)
.
and_return
(
nil
)
end
it
'returns processing'
do
expect
(
result
).
to
eq
(
status: :processing
,
last_step: :pod_logs
)
end
end
end
end
end
lib/api/group_clusters.rb
View file @
cd3c7e76
...
...
@@ -59,7 +59,7 @@ module API
requires
:token
,
type:
String
,
desc:
'Token to authenticate against Kubernetes'
optional
:ca_cert
,
type:
String
,
desc:
'TLS certificate (needed if API is using a self-signed TLS certificate)'
optional
:namespace
,
type:
String
,
desc:
'Unique namespace related to Group'
optional
:authorization_type
,
type:
String
,
values:
Clusters
::
Platforms
::
Kubernetes
.
authorization_types
.
keys
,
default:
'rbac'
,
desc:
'Cluster authorization type, defaults to RBAC'
optional
:authorization_type
,
type:
String
,
values:
::
Clusters
::
Platforms
::
Kubernetes
.
authorization_types
.
keys
,
default:
'rbac'
,
desc:
'Cluster authorization type, defaults to RBAC'
end
use
:create_params_ee
end
...
...
@@ -96,7 +96,7 @@ module API
put
':id/clusters/:cluster_id'
do
authorize!
:update_cluster
,
cluster
update_service
=
Clusters
::
UpdateService
.
new
(
current_user
,
update_cluster_params
)
update_service
=
::
Clusters
::
UpdateService
.
new
(
current_user
,
update_cluster_params
)
if
update_service
.
execute
(
cluster
)
present
cluster
,
with:
Entities
::
ClusterGroup
...
...
lib/api/project_clusters.rb
View file @
cd3c7e76
...
...
@@ -62,7 +62,7 @@ module API
requires
:token
,
type:
String
,
desc:
'Token to authenticate against Kubernetes'
optional
:ca_cert
,
type:
String
,
desc:
'TLS certificate (needed if API is using a self-signed TLS certificate)'
optional
:namespace
,
type:
String
,
desc:
'Unique namespace related to Project'
optional
:authorization_type
,
type:
String
,
values:
Clusters
::
Platforms
::
Kubernetes
.
authorization_types
.
keys
,
default:
'rbac'
,
desc:
'Cluster authorization type, defaults to RBAC'
optional
:authorization_type
,
type:
String
,
values:
::
Clusters
::
Platforms
::
Kubernetes
.
authorization_types
.
keys
,
default:
'rbac'
,
desc:
'Cluster authorization type, defaults to RBAC'
end
use
:create_params_ee
end
...
...
@@ -100,7 +100,7 @@ module API
put
':id/clusters/:cluster_id'
do
authorize!
:update_cluster
,
cluster
update_service
=
Clusters
::
UpdateService
.
new
(
current_user
,
update_cluster_params
)
update_service
=
::
Clusters
::
UpdateService
.
new
(
current_user
,
update_cluster_params
)
if
update_service
.
execute
(
cluster
)
present
cluster
,
with:
Entities
::
ClusterProject
...
...
locale/gitlab.pot
View file @
cd3c7e76
...
...
@@ -5068,6 +5068,9 @@ msgstr ""
msgid "Container Scanning"
msgstr ""
msgid "Container does not exist"
msgstr ""
msgid "Container registry images"
msgstr ""
...
...
@@ -12659,6 +12662,9 @@ msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
msgid "No containers available"
msgstr ""
msgid "No contributions"
msgstr ""
...
...
@@ -13999,6 +14005,9 @@ msgstr ""
msgid "Please wait while we import the repository for you. Refresh at will."
msgstr ""
msgid "Pod does not exist"
msgstr ""
msgid "Pod logs"
msgstr ""
...
...
@@ -20386,6 +20395,9 @@ msgstr ""
msgid "Unable to collect memory info"
msgstr ""
msgid "Unable to connect to Elasticsearch"
msgstr ""
msgid "Unable to connect to Prometheus server"
msgstr ""
...
...
@@ -20455,6 +20467,9 @@ msgstr ""
msgid "Unknown Error"
msgstr ""
msgid "Unknown cache key"
msgstr ""
msgid "Unknown encryption strategy: %{encrypted_strategy}!"
msgstr ""
...
...
spec/support/helpers/kubernetes_helpers.rb
View file @
cd3c7e76
...
...
@@ -101,7 +101,7 @@ module KubernetesHelpers
end
logs_url
=
service
.
api_url
+
"/api/v1/namespaces/
#{
namespace
}
/pods/
#{
pod_name
}
"
\
"/log?
#{
container_query_param
}
tailLines=
#{
Clusters
::
Platforms
::
Kubernetes
::
LOGS_LIMIT
}
×tamps=true"
"/log?
#{
container_query_param
}
tailLines=
#{
::
PodLogs
::
KubernetesService
::
LOGS_LIMIT
}
×tamps=true"
if
status
response
=
{
status:
status
}
...
...
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