1# frozen_string_literal: true
2
3module Clusters
4  module Aws
5    class FinalizeCreationService
6      include Gitlab::Utils::StrongMemoize
7
8      attr_reader :provider
9
10      delegate :cluster, to: :provider
11
12      def execute(provider)
13        @provider = provider
14
15        configure_provider
16        create_gitlab_service_account!
17        configure_platform_kubernetes
18        configure_node_authentication!
19
20        cluster.save!
21      rescue ::Aws::CloudFormation::Errors::ServiceError => e
22        log_service_error(e.class.name, provider.id, e.message)
23        provider.make_errored!(s_('ClusterIntegration|Failed to fetch CloudFormation stack: %{message}') % { message: e.message })
24      rescue Kubeclient::HttpError => e
25        log_service_error(e.class.name, provider.id, e.message)
26        provider.make_errored!(s_('ClusterIntegration|Failed to run Kubeclient: %{message}') % { message: e.message })
27      rescue ActiveRecord::RecordInvalid => e
28        log_service_error(e.class.name, provider.id, e.message)
29        provider.make_errored!(s_('ClusterIntegration|Failed to configure EKS provider: %{message}') % { message: e.message })
30      end
31
32      private
33
34      def create_gitlab_service_account!
35        Clusters::Kubernetes::CreateOrUpdateServiceAccountService.gitlab_creator(
36          kube_client,
37          rbac: true
38        ).execute
39      end
40
41      def configure_provider
42        provider.status_event = :make_created
43      end
44
45      def configure_platform_kubernetes
46        cluster.build_platform_kubernetes(
47          api_url: cluster_endpoint,
48          ca_cert: cluster_certificate,
49          token: request_kubernetes_token)
50      end
51
52      def request_kubernetes_token
53        Clusters::Kubernetes::FetchKubernetesTokenService.new(
54          kube_client,
55          Clusters::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
56          Clusters::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE
57        ).execute
58      end
59
60      def kube_client
61        @kube_client ||= build_kube_client!(
62          cluster_endpoint,
63          cluster_certificate
64        )
65      end
66
67      def build_kube_client!(api_url, ca_pem)
68        raise "Incomplete settings" unless api_url
69
70        Gitlab::Kubernetes::KubeClient.new(
71          api_url,
72          auth_options: kubeclient_auth_options,
73          ssl_options: kubeclient_ssl_options(ca_pem),
74          http_proxy_uri: ENV['http_proxy']
75        )
76      end
77
78      def kubeclient_auth_options
79        { bearer_token: Kubeclient::AmazonEksCredentials.token(provider.credentials, cluster.name) }
80      end
81
82      def kubeclient_ssl_options(ca_pem)
83        opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
84
85        if ca_pem.present?
86          opts[:cert_store] = OpenSSL::X509::Store.new
87          opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
88        end
89
90        opts
91      end
92
93      def cluster_stack
94        @cluster_stack ||= provider.api_client.describe_stacks(stack_name: provider.cluster.name).stacks.first
95      end
96
97      def stack_output_value(key)
98        cluster_stack.outputs.detect { |output| output.output_key == key }.output_value
99      end
100
101      def node_instance_role_arn
102        stack_output_value('NodeInstanceRole')
103      end
104
105      def cluster_endpoint
106        strong_memoize(:cluster_endpoint) do
107          stack_output_value('ClusterEndpoint')
108        end
109      end
110
111      def cluster_certificate
112        strong_memoize(:cluster_certificate) do
113          Base64.decode64(stack_output_value('ClusterCertificate'))
114        end
115      end
116
117      def configure_node_authentication!
118        kube_client.create_config_map(node_authentication_config)
119      end
120
121      def node_authentication_config
122        Gitlab::Kubernetes::ConfigMaps::AwsNodeAuth.new(node_instance_role_arn).generate
123      end
124
125      def logger
126        @logger ||= Gitlab::Kubernetes::Logger.build
127      end
128
129      def log_service_error(exception, provider_id, message)
130        logger.error(
131          exception: exception.class.name,
132          service: self.class.name,
133          provider_id: provider_id,
134          message: message
135        )
136      end
137    end
138  end
139end
140