Welcome to mirror list, hosted at ThFree Co, Russian Federation.

kubernetes_client.rb « tooling « lib « tooling - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f7abc5ac4cf5f25fe2430616d703fa3660ab637d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# frozen_string_literal: true

require 'json'
require 'time'
require_relative '../../../lib/gitlab/popen' unless defined?(Gitlab::Popen)

module Tooling
  class KubernetesClient
    RESOURCE_LIST = 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa,crd'
    CommandFailedError = Class.new(StandardError)

    attr_reader :namespace

    def initialize(namespace:)
      @namespace = namespace
    end

    def cleanup_by_release(release_name:, wait: true)
      delete_by_selector(release_name: release_name, wait: wait)
      delete_by_matching_name(release_name: release_name)
    end

    def cleanup_by_created_at(resource_type:, created_before:, wait: true)
      resource_names = resource_names_created_before(resource_type: resource_type, created_before: created_before)
      delete_by_exact_names(resource_type: resource_type, resource_names: resource_names, wait: wait)
    end

    private

    def delete_by_selector(release_name:, wait:)
      selector = case release_name
                 when String
                   %(-l release="#{release_name}")
                 when Array
                   %(-l 'release in (#{release_name.join(', ')})')
                 else
                   raise ArgumentError, 'release_name must be a string or an array'
                 end

      command = [
        'delete',
        RESOURCE_LIST,
        %(--namespace "#{namespace}"),
        '--now',
        '--ignore-not-found',
        '--include-uninitialized',
        %(--wait=#{wait}),
        selector
      ]

      run_command(command)
    end

    def delete_by_exact_names(resource_names:, wait:, resource_type: nil)
      command = [
        'delete',
        resource_type,
        %(--namespace "#{namespace}"),
        '--now',
        '--ignore-not-found',
        '--include-uninitialized',
        %(--wait=#{wait}),
        resource_names.join(' ')
      ]

      run_command(command)
    end

    def delete_by_matching_name(release_name:)
      resource_names = raw_resource_names
      command = [
        'delete',
        %(--namespace "#{namespace}"),
        '--ignore-not-found'
      ]

      Array(release_name).each do |release|
        resource_names
          .select { |resource_name| resource_name.include?(release) }
          .each { |matching_resource| run_command(command + [matching_resource]) }
      end
    end

    def raw_resource_names
      command = [
        'get',
        RESOURCE_LIST,
        %(--namespace "#{namespace}"),
        '-o name'
      ]
      run_command(command).lines.map(&:strip)
    end

    def resource_names_created_before(resource_type:, created_before:)
      command = [
        'get',
        resource_type,
        %(--namespace "#{namespace}"),
        "--sort-by='{.metadata.creationTimestamp}'",
        '-o json'
      ]

      response = run_command(command)
      JSON.parse(response)['items'] # rubocop:disable Gitlab/Json
        .map { |resource| resource.dig('metadata', 'name') if Time.parse(resource.dig('metadata', 'creationTimestamp')) < created_before }
        .compact
    rescue ::JSON::ParserError => ex
      puts "Ignoring this JSON parsing error: #{ex}\n\nResponse was:\n#{response}" # rubocop:disable Rails/Output
      []
    end

    def run_command(command)
      final_command = ['kubectl', *command.compact].join(' ')
      puts "Running command: `#{final_command}`" # rubocop:disable Rails/Output

      result = Gitlab::Popen.popen_with_detail([final_command])

      if result.status.success?
        result.stdout.chomp.freeze
      else
        raise CommandFailedError, "The `#{final_command}` command failed (status: #{result.status}) with the following error:\n#{result.stderr}"
      end
    end
  end
end