1# frozen_string_literal: true
2
3module PodLogs
4  class ElasticsearchService < PodLogs::BaseService
5    steps :check_arguments,
6          :get_raw_pods,
7          :get_pod_names,
8          :check_times,
9          :check_search,
10          :check_cursor,
11          :pod_logs,
12          :filter_return_keys
13
14    self.reactive_cache_worker_finder = ->(id, _cache_key, namespace, params) { new(::Clusters::Cluster.find(id), namespace, params: params) }
15
16    private
17
18    def valid_params
19      super + %w(search start_time end_time cursor)
20    end
21
22    def success_return_keys
23      super + %i(cursor)
24    end
25
26    def get_raw_pods(result)
27      client = cluster&.elasticsearch_client
28      return error(_('Unable to connect to Elasticsearch')) unless client
29
30      result[:raw_pods] = ::Gitlab::Elasticsearch::Logs::Pods.new(client).pods(namespace)
31
32      success(result)
33    rescue Elasticsearch::Transport::Transport::ServerError => e
34      ::Gitlab::ErrorTracking.track_exception(e)
35
36      error(_('Elasticsearch returned status code: %{status_code}') % {
37        # ServerError is the parent class of exceptions named after HTTP status codes, eg: "Elasticsearch::Transport::Transport::Errors::NotFound"
38        # there is no method on the exception other than the class name to determine the type of error encountered.
39        status_code: e.class.name.split('::').last
40      })
41    end
42
43    def check_times(result)
44      result[:start_time] = params['start_time'] if params.key?('start_time') && Time.iso8601(params['start_time'])
45      result[:end_time] = params['end_time'] if params.key?('end_time') && Time.iso8601(params['end_time'])
46
47      success(result)
48    rescue ArgumentError
49      error(_('Invalid start or end time format'))
50    end
51
52    def check_search(result)
53      result[:search] = params['search'] if params.key?('search')
54
55      return error(_('Invalid search parameter')) if result[:search] && !result[:search].is_a?(String)
56
57      success(result)
58    end
59
60    def check_cursor(result)
61      result[:cursor] = params['cursor'] if params.key?('cursor')
62
63      return error(_('Invalid cursor parameter')) if result[:cursor] && !result[:cursor].is_a?(String)
64
65      success(result)
66    end
67
68    def pod_logs(result)
69      client = cluster&.elasticsearch_client
70      return error(_('Unable to connect to Elasticsearch')) unless client
71
72      response = ::Gitlab::Elasticsearch::Logs::Lines.new(client).pod_logs(
73        namespace,
74        pod_name: result[:pod_name],
75        container_name: result[:container_name],
76        search: result[:search],
77        start_time: result[:start_time],
78        end_time: result[:end_time],
79        cursor: result[:cursor],
80        chart_above_v2: cluster.elastic_stack_adapter.chart_above_v2?
81      )
82
83      result.merge!(response)
84
85      success(result)
86    rescue Elasticsearch::Transport::Transport::ServerError => e
87      ::Gitlab::ErrorTracking.track_exception(e)
88
89      error(_('Elasticsearch returned status code: %{status_code}') % {
90        # ServerError is the parent class of exceptions named after HTTP status codes, eg: "Elasticsearch::Transport::Transport::Errors::NotFound"
91        # there is no method on the exception other than the class name to determine the type of error encountered.
92        status_code: e.class.name.split('::').last
93      })
94    rescue ::Gitlab::Elasticsearch::Logs::Lines::InvalidCursor
95      error(_('Invalid cursor value provided'))
96    end
97  end
98end
99