summaryrefslogtreecommitdiff
path: root/app/models/project_services/kubernetes_service.rb
blob: f5fbf8b353b375cd073baab96bd49aec12ae4906 (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
class KubernetesService < DeploymentService
  # Namespace defaults to the project path, but can be overridden in case that
  # is an invalid or inappropriate name
  prop_accessor :namespace

  #  Access to kubernetes is directly through the API
  prop_accessor :api_url

  # Bearer authentication
  # TODO:  user/password auth, client certificates
  prop_accessor :token

  # Provide a custom CA bundle for self-signed deployments
  prop_accessor :ca_pem

  with_options presence: true, if: :activated? do
    validates :api_url, url: true
    validates :token

    validates :namespace,
      format: {
        with: Gitlab::Regex.kubernetes_namespace_regex,
        message: Gitlab::Regex.kubernetes_namespace_regex_message,
      },
      length: 1..63
  end

  def initialize_properties
    if properties.nil?
      self.properties = {}
      self.namespace = project.path if project.present?
    end
  end

  def title
    'Kubernetes'
  end

  def description
    'Kubernetes / Openshift integration'
  end

  def help
    ''
  end

  def to_param
    'kubernetes'
  end

  def fields
    [
        { type: 'text',
          name: 'namespace',
          title: 'Kubernetes namespace',
          placeholder: 'Kubernetes namespace',
        },
        { type: 'text',
          name: 'api_url',
          title: 'API URL',
          placeholder: 'Kubernetes API URL, like https://kube.example.com/',
        },
        { type: 'text',
          name: 'token',
          title: 'Service token',
          placeholder: 'Service token',
        },
        { type: 'textarea',
          name: 'ca_pem',
          title: 'Custom CA bundle',
          placeholder: 'Certificate Authority bundle (PEM format)',
        },
    ]
  end

  # Check we can connect to the Kubernetes API
  def test(*args)
    kubeclient = build_kubeclient
    kubeclient.discover

    { success: kubeclient.discovered, result: "Checked API discovery endpoint" }
  rescue => err
    { success: false, result: err }
  end

  def predefined_variables
    variables = [
      { key: 'KUBE_URL', value: api_url, public: true },
      { key: 'KUBE_TOKEN', value: token, public: false },
      { key: 'KUBE_NAMESPACE', value: namespace, public: true }
    ]
    variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true } if ca_pem.present?
    variables
  end

  private

  def build_kubeclient(api_path = '/api', api_version = 'v1')
    return nil unless api_url && namespace && token

    url = URI.parse(api_url)
    url.path = url.path[0..-2] if url.path[-1] == "/"
    url.path += api_path

    ::Kubeclient::Client.new(
      url,
      api_version,
      ssl_options: kubeclient_ssl_options,
      auth_options: kubeclient_auth_options,
      http_proxy_uri: ENV['http_proxy']
    )
  end

  def kubeclient_ssl_options
    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 kubeclient_auth_options
    { bearer_token: token }
  end
end