2020-03-11 11:09:37 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
describe ::PodLogs::KubernetesService do
|
|
|
|
include KubernetesHelpers
|
|
|
|
|
|
|
|
let_it_be(:cluster) { create(:cluster, :provided_by_gcp, environment_scope: '*') }
|
|
|
|
let(:namespace) { 'autodevops-deploy-9-production' }
|
|
|
|
|
|
|
|
let(:pod_name) { 'pod-1' }
|
2020-03-18 02:09:38 -04:00
|
|
|
let(:pod_name_2) { 'pod-2' }
|
2020-03-17 23:09:43 -04:00
|
|
|
let(:container_name) { 'container-0' }
|
2020-03-18 02:09:38 -04:00
|
|
|
let(:container_name_2) { 'foo-0' }
|
2020-03-11 11:09:37 -04:00
|
|
|
let(:params) { {} }
|
|
|
|
|
|
|
|
let(:raw_logs) do
|
|
|
|
"2019-12-13T14:04:22.123456Z Log 1\n2019-12-13T14:04:23.123456Z Log 2\n" \
|
|
|
|
"2019-12-13T14:04:24.123456Z Log 3"
|
|
|
|
end
|
2020-03-18 02:09:38 -04:00
|
|
|
|
2020-03-17 23:09:43 -04:00
|
|
|
let(:raw_pods) do
|
|
|
|
JSON.parse([
|
2020-03-18 02:09:38 -04:00
|
|
|
kube_pod(name: pod_name),
|
|
|
|
kube_pod(name: pod_name_2, container_name: container_name_2)
|
2020-03-17 23:09:43 -04:00
|
|
|
].to_json, object_class: OpenStruct)
|
|
|
|
end
|
2020-03-11 11:09:37 -04:00
|
|
|
|
|
|
|
subject { described_class.new(cluster, namespace, params: params) }
|
|
|
|
|
|
|
|
describe '#pod_logs' do
|
|
|
|
let(:result_arg) do
|
|
|
|
{
|
|
|
|
pod_name: pod_name,
|
|
|
|
container_name: container_name
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:expected_logs) { raw_logs }
|
|
|
|
let(:service) { create(:cluster_platform_kubernetes, :configured) }
|
|
|
|
|
|
|
|
it 'returns the logs' do
|
|
|
|
stub_kubeclient_logs(pod_name, 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
|
|
|
|
|
|
|
|
describe '#encode_logs_to_utf8', :aggregate_failures do
|
|
|
|
let(:service) { create(:cluster_platform_kubernetes, :configured) }
|
|
|
|
let(:expected_logs) { '2019-12-13T14:04:22.123456Z ✔ Started logging errors to Sentry' }
|
|
|
|
let(:raw_logs) { expected_logs.dup.force_encoding(Encoding::ASCII_8BIT) }
|
|
|
|
let(:result) { subject.send(:encode_logs_to_utf8, result_arg) }
|
|
|
|
|
|
|
|
let(:result_arg) do
|
|
|
|
{
|
|
|
|
pod_name: pod_name,
|
|
|
|
container_name: container_name,
|
|
|
|
logs: raw_logs
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'converts logs to utf-8' do
|
|
|
|
expect(result[:status]).to eq(:success)
|
|
|
|
expect(result[:logs]).to eq(expected_logs)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns error if output of encoding helper is blank' do
|
|
|
|
allow(Gitlab::EncodingHelper).to receive(:encode_utf8).and_return('')
|
|
|
|
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
|
|
expect(result[:message]).to eq('Unable to convert Kubernetes logs encoding to UTF-8')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns error if output of encoding helper is nil' do
|
|
|
|
allow(Gitlab::EncodingHelper).to receive(:encode_utf8).and_return(nil)
|
|
|
|
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
|
|
expect(result[:message]).to eq('Unable to convert Kubernetes logs encoding to UTF-8')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns error if output of encoding helper is not UTF-8' do
|
|
|
|
allow(Gitlab::EncodingHelper).to receive(:encode_utf8)
|
|
|
|
.and_return(expected_logs.encode(Encoding::UTF_16BE))
|
|
|
|
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
|
|
expect(result[:message]).to eq('Unable to convert Kubernetes logs encoding to UTF-8')
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when logs are nil' do
|
|
|
|
let(:raw_logs) { nil }
|
|
|
|
let(:expected_logs) { nil }
|
|
|
|
|
|
|
|
it 'returns nil' do
|
|
|
|
expect(result[:status]).to eq(:success)
|
|
|
|
expect(result[:logs]).to eq(expected_logs)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when logs are blank' do
|
|
|
|
let(:raw_logs) { (+'').force_encoding(Encoding::ASCII_8BIT) }
|
|
|
|
let(:expected_logs) { '' }
|
|
|
|
|
|
|
|
it 'returns blank string' do
|
|
|
|
expect(result[:status]).to eq(:success)
|
|
|
|
expect(result[:logs]).to eq(expected_logs)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when logs are already in utf-8' do
|
|
|
|
let(:raw_logs) { expected_logs }
|
|
|
|
|
|
|
|
it 'does not fail' do
|
|
|
|
expect(result[:status]).to eq(:success)
|
|
|
|
expect(result[:logs]).to eq(expected_logs)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#split_logs' do
|
|
|
|
let(:service) { create(:cluster_platform_kubernetes, :configured) }
|
|
|
|
|
|
|
|
let(:expected_logs) do
|
|
|
|
[
|
2020-03-17 23:09:43 -04:00
|
|
|
{ message: "Log 1", pod: 'pod-1', timestamp: "2019-12-13T14:04:22.123456Z" },
|
|
|
|
{ message: "Log 2", pod: 'pod-1', timestamp: "2019-12-13T14:04:23.123456Z" },
|
|
|
|
{ message: "Log 3", pod: 'pod-1', timestamp: "2019-12-13T14:04:24.123456Z" }
|
2020-03-11 11:09:37 -04:00
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:result_arg) do
|
|
|
|
{
|
|
|
|
pod_name: pod_name,
|
|
|
|
container_name: container_name,
|
|
|
|
logs: raw_logs
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns the logs' do
|
|
|
|
result = subject.send(:split_logs, result_arg)
|
|
|
|
|
|
|
|
aggregate_failures do
|
|
|
|
expect(result[:status]).to eq(:success)
|
|
|
|
expect(result[:logs]).to eq(expected_logs)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-03-17 23:09:43 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
it 'returns error if pod_name is too long' do
|
|
|
|
result = subject.send(:check_pod_name, pod_name: "a very long string." * 15, pods: [pod_name])
|
|
|
|
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
|
|
expect(result[:message]).to eq('pod_name cannot be larger than 253 chars')
|
|
|
|
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,
|
2020-03-18 02:09:38 -04:00
|
|
|
pod_name: pod_name_2,
|
2020-03-17 23:09:43 -04:00
|
|
|
raw_pods: raw_pods
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(result[:status]).to eq(:success)
|
2020-03-18 02:09:38 -04:00
|
|
|
expect(result[:container_name]).to eq(container_name_2)
|
2020-03-17 23:09:43 -04:00
|
|
|
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
|
|
|
|
|
|
|
|
it 'returns error if container_name is too long' do
|
|
|
|
result = subject.send(:check_container_name,
|
|
|
|
container_name: "a very long string." * 15,
|
|
|
|
pod_name: pod_name,
|
|
|
|
raw_pods: raw_pods
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
|
|
expect(result[:message]).to eq('container_name cannot be larger than 253 chars')
|
|
|
|
end
|
|
|
|
end
|
2020-03-11 11:09:37 -04:00
|
|
|
end
|