summaryrefslogtreecommitdiff
path: root/qa/qa/service/kubernetes_cluster.rb
blob: 77f144ff06e38fb694a1b82b76d5553ef1a3a4eb (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
require 'securerandom'
require 'mkmf'
require 'pathname'

module QA
  module Service
    class KubernetesCluster
      include Service::Shellout

      attr_reader :api_url, :ca_certificate, :token, :rbac

      def initialize(rbac: false)
        @rbac = rbac
      end

      def cluster_name
        @cluster_name ||= "qa-cluster-#{SecureRandom.hex(4)}-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}"
      end

      def create!
        validate_dependencies
        login_if_not_already_logged_in

        shell <<~CMD.tr("\n", ' ')
          gcloud container clusters
          create #{cluster_name}
          #{auth_options}
          --enable-basic-auth
          --zone #{Runtime::Env.gcloud_zone}
          && gcloud container clusters
          get-credentials
          --zone #{Runtime::Env.gcloud_zone}
          #{cluster_name}
        CMD

        @api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'`

        @admin_user = "#{cluster_name}-admin"
        master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --zone #{Runtime::Env.gcloud_zone} --format 'json(masterAuth.username, masterAuth.password)'`)
        shell <<~CMD.tr("\n", ' ')
          kubectl config set-credentials #{@admin_user}
          --username #{master_auth['masterAuth']['username']}
          --password #{master_auth['masterAuth']['password']}
        CMD

        if rbac
          create_service_account

          secrets = JSON.parse(`kubectl get secrets -o json`)
          gitlab_account = secrets['items'].find do |item|
            item['metadata']['annotations']['kubernetes.io/service-account.name'] == 'gitlab-account'
          end

          @ca_certificate = Base64.decode64(gitlab_account['data']['ca.crt'])
          @token = Base64.decode64(gitlab_account['data']['token'])
        else
          @ca_certificate = Base64.decode64(`kubectl get secrets -o jsonpath="{.items[0].data['ca\\.crt']}"`)
          @token = Base64.decode64(`kubectl get secrets -o jsonpath='{.items[0].data.token}'`)
        end

        self
      end

      def remove!
        shell <<~CMD.tr("\n", ' ')
          gcloud container clusters delete
          --zone #{Runtime::Env.gcloud_zone}
	  #{cluster_name}
	  --quiet --async
	CMD
      end

      private

      def create_service_account
        shell('kubectl create -f -', stdin_data: service_account)
        shell("kubectl --user #{@admin_user} create -f -", stdin_data: service_account_role_binding)
      end

      def service_account
        <<~YAML
          apiVersion: v1
          kind: ServiceAccount
          metadata:
            name: gitlab-account
            namespace: default
        YAML
      end

      def service_account_role_binding
        <<~YAML
          kind: ClusterRoleBinding
          apiVersion: rbac.authorization.k8s.io/v1
          metadata:
            name: gitlab-account-binding
          subjects:
          - kind: ServiceAccount
            name: gitlab-account
            namespace: default
          roleRef:
            kind: ClusterRole
            name: cluster-admin
            apiGroup: rbac.authorization.k8s.io
        YAML
      end

      def auth_options
        "--enable-legacy-authorization" unless rbac
      end

      def validate_dependencies
        find_executable('gcloud') || raise(_("You must first install `gcloud` executable to run these tests."))
        find_executable('kubectl') || raise(_("You must first install `kubectl` executable to run these tests."))
      end

      def login_if_not_already_logged_in
        if Runtime::Env.has_gcloud_credentials?
          attempt_login_with_env_vars
        else
          account = `gcloud auth list --filter=status:ACTIVE --format="value(account)"`
          if account.empty?
            raise _("Failed to login to gcloud. No credentials provided in environment and no credentials found locally.")
          else
            puts "gcloud account found. Using: #{account} for creating K8s cluster."
          end
        end
      end

      def attempt_login_with_env_vars
        puts _("No gcloud account. Attempting to login from env vars GCLOUD_ACCOUNT_EMAIL and GCLOUD_ACCOUNT_KEY.")
        gcloud_account_key = Tempfile.new('gcloud-account-key')
        gcloud_account_key.write(Runtime::Env.gcloud_account_key)
        gcloud_account_key.close
        gcloud_account_email = Runtime::Env.gcloud_account_email
        shell("gcloud auth activate-service-account #{gcloud_account_email} --key-file #{gcloud_account_key.path}")
      ensure
        gcloud_account_key && gcloud_account_key.unlink
      end
    end
  end
end