summaryrefslogtreecommitdiff
path: root/app/services/clusters/aws/finalize_creation_service.rb
blob: 54f07e1d44c52a4916b1899d8d6898b640549e47 (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
# frozen_string_literal: true

module Clusters
  module Aws
    class FinalizeCreationService
      include Gitlab::Utils::StrongMemoize

      attr_reader :provider

      delegate :cluster, to: :provider

      def execute(provider)
        @provider = provider

        configure_provider
        create_gitlab_service_account!
        configure_platform_kubernetes
        configure_node_authentication!

        cluster.save!
      rescue ::Aws::CloudFormation::Errors::ServiceError => e
        log_service_error(e.class.name, provider.id, e.message)
        provider.make_errored!(s_('ClusterIntegration|Failed to fetch CloudFormation stack: %{message}') % { message: e.message })
      rescue Kubeclient::HttpError => e
        log_service_error(e.class.name, provider.id, e.message)
        provider.make_errored!(s_('ClusterIntegration|Failed to run Kubeclient: %{message}') % { message: e.message })
      rescue ActiveRecord::RecordInvalid => e
        log_service_error(e.class.name, provider.id, e.message)
        provider.make_errored!(s_('ClusterIntegration|Failed to configure EKS provider: %{message}') % { message: e.message })
      end

      private

      def create_gitlab_service_account!
        Clusters::Kubernetes::CreateOrUpdateServiceAccountService.gitlab_creator(
          kube_client,
          rbac: true
        ).execute
      end

      def configure_provider
        provider.status_event = :make_created
      end

      def configure_platform_kubernetes
        cluster.build_platform_kubernetes(
          api_url: cluster_endpoint,
          ca_cert: cluster_certificate,
          token: request_kubernetes_token)
      end

      def request_kubernetes_token
        Clusters::Kubernetes::FetchKubernetesTokenService.new(
          kube_client,
          Clusters::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
          Clusters::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE
        ).execute
      end

      def kube_client
        @kube_client ||= build_kube_client!(
          cluster_endpoint,
          cluster_certificate
        )
      end

      def build_kube_client!(api_url, ca_pem)
        raise "Incomplete settings" unless api_url

        Gitlab::Kubernetes::KubeClient.new(
          api_url,
          auth_options: kubeclient_auth_options,
          ssl_options: kubeclient_ssl_options(ca_pem),
          http_proxy_uri: ENV['http_proxy']
        )
      end

      def kubeclient_auth_options
        { bearer_token: Kubeclient::AmazonEksCredentials.token(provider.credentials, cluster.name) }
      end

      def kubeclient_ssl_options(ca_pem)
        opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }

        if ca_pem.present?
          opts[:cert_store] = OpenSSL::X509::Store.new
          opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
        end

        opts
      end

      def cluster_stack
        @cluster_stack ||= provider.api_client.describe_stacks(stack_name: provider.cluster.name).stacks.first
      end

      def stack_output_value(key)
        cluster_stack.outputs.detect { |output| output.output_key == key }.output_value
      end

      def node_instance_role_arn
        stack_output_value('NodeInstanceRole')
      end

      def cluster_endpoint
        strong_memoize(:cluster_endpoint) do
          stack_output_value('ClusterEndpoint')
        end
      end

      def cluster_certificate
        strong_memoize(:cluster_certificate) do
          Base64.decode64(stack_output_value('ClusterCertificate'))
        end
      end

      def configure_node_authentication!
        kube_client.create_config_map(node_authentication_config)
      end

      def node_authentication_config
        Gitlab::Kubernetes::ConfigMaps::AwsNodeAuth.new(node_instance_role_arn).generate
      end

      def logger
        @logger ||= Gitlab::Kubernetes::Logger.build
      end

      def log_service_error(exception, provider_id, message)
        logger.error(
          exception: exception.class.name,
          service: self.class.name,
          provider_id: provider_id,
          message: message
        )
      end
    end
  end
end