summaryrefslogtreecommitdiff
path: root/lib/gitlab/kubernetes
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/kubernetes')
-rw-r--r--lib/gitlab/kubernetes/helm.rb2
-rw-r--r--lib/gitlab/kubernetes/helm/api.rb6
-rw-r--r--lib/gitlab/kubernetes/helm/delete_command.rb2
-rw-r--r--lib/gitlab/kubernetes/helm/parsers/list_v2.rb37
-rw-r--r--lib/gitlab/kubernetes/kube_client.rb72
-rw-r--r--lib/gitlab/kubernetes/network_policy.rb91
6 files changed, 172 insertions, 38 deletions
diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb
index 3e201d68297..00ab7109267 100644
--- a/lib/gitlab/kubernetes/helm.rb
+++ b/lib/gitlab/kubernetes/helm.rb
@@ -3,7 +3,7 @@
module Gitlab
module Kubernetes
module Helm
- HELM_VERSION = '2.16.3'
+ HELM_VERSION = '2.16.6'
KUBECTL_VERSION = '1.13.12'
NAMESPACE = 'gitlab-managed-apps'
NAMESPACE_LABELS = { 'app.gitlab.com/managed_by' => :gitlab }.freeze
diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb
index 3b843799d66..ceda18442d6 100644
--- a/lib/gitlab/kubernetes/helm/api.rb
+++ b/lib/gitlab/kubernetes/helm/api.rb
@@ -99,11 +99,7 @@ module Gitlab
command.cluster_role_binding_resource.tap do |cluster_role_binding_resource|
break unless cluster_role_binding_resource
- if cluster_role_binding_exists?(cluster_role_binding_resource)
- kubeclient.update_cluster_role_binding(cluster_role_binding_resource)
- else
- kubeclient.create_cluster_role_binding(cluster_role_binding_resource)
- end
+ kubeclient.update_cluster_role_binding(cluster_role_binding_resource)
end
end
diff --git a/lib/gitlab/kubernetes/helm/delete_command.rb b/lib/gitlab/kubernetes/helm/delete_command.rb
index 9d0fd30ba8f..771444ee9ee 100644
--- a/lib/gitlab/kubernetes/helm/delete_command.rb
+++ b/lib/gitlab/kubernetes/helm/delete_command.rb
@@ -36,8 +36,6 @@ module Gitlab
@rbac
end
- private
-
def delete_command
command = ['helm', 'delete', '--purge', name] + tls_flags_if_remote_tiller
diff --git a/lib/gitlab/kubernetes/helm/parsers/list_v2.rb b/lib/gitlab/kubernetes/helm/parsers/list_v2.rb
new file mode 100644
index 00000000000..c5c5d198a6c
--- /dev/null
+++ b/lib/gitlab/kubernetes/helm/parsers/list_v2.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ module Helm
+ module Parsers
+ # Parses Helm v2 list (JSON) output
+ class ListV2
+ ParserError = Class.new(StandardError)
+
+ attr_reader :contents, :json
+
+ def initialize(contents)
+ @contents = contents
+ @json = Gitlab::Json.parse(contents)
+ rescue JSON::ParserError => e
+ raise ParserError, e.message
+ end
+
+ def releases
+ @releases = helm_releases
+ end
+
+ private
+
+ def helm_releases
+ helm_releases = json['Releases'] || []
+
+ raise ParserError, 'Invalid format for Releases' unless helm_releases.all? { |item| item.is_a?(Hash) }
+
+ helm_releases
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb
index 7c5525b982c..2110d586d30 100644
--- a/lib/gitlab/kubernetes/kube_client.rb
+++ b/lib/gitlab/kubernetes/kube_client.rb
@@ -19,7 +19,9 @@ module Gitlab
apps: { group: 'apis/apps', version: 'v1' },
extensions: { group: 'apis/extensions', version: 'v1beta1' },
istio: { group: 'apis/networking.istio.io', version: 'v1alpha3' },
- knative: { group: 'apis/serving.knative.dev', version: 'v1alpha1' }
+ knative: { group: 'apis/serving.knative.dev', version: 'v1alpha1' },
+ metrics: { group: 'apis/metrics.k8s.io', version: 'v1beta1' },
+ networking: { group: 'apis/networking.k8s.io', version: 'v1' }
}.freeze
SUPPORTED_API_GROUPS.each do |name, params|
@@ -33,7 +35,8 @@ module Gitlab
end
# Core API methods delegates to the core api group client
- delegate :get_pods,
+ delegate :get_nodes,
+ :get_pods,
:get_secrets,
:get_config_map,
:get_namespace,
@@ -56,9 +59,7 @@ module Gitlab
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
# group client
- delegate :create_cluster_role_binding,
- :get_cluster_role_binding,
- :update_cluster_role_binding,
+ delegate :update_cluster_role_binding,
to: :rbac_client
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
@@ -70,9 +71,7 @@ module Gitlab
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
# group client
- delegate :create_role_binding,
- :get_role_binding,
- :update_role_binding,
+ delegate :update_role_binding,
to: :rbac_client
# non-entity methods that can only work with the core client
@@ -88,6 +87,14 @@ module Gitlab
:update_gateway,
to: :istio_client
+ # NetworkPolicy methods delegate to the apis/networking.k8s.io api
+ # group client
+ delegate :create_network_policy,
+ :get_network_policies,
+ :update_network_policy,
+ :delete_network_policy,
+ to: :networking_client
+
attr_reader :api_prefix, :kubeclient_options
DEFAULT_KUBECLIENT_OPTIONS = {
@@ -97,6 +104,31 @@ module Gitlab
}
}.freeze
+ def self.graceful_request(cluster_id)
+ { status: :connected, response: yield }
+ rescue *Gitlab::Kubernetes::Errors::CONNECTION
+ { status: :unreachable }
+ rescue *Gitlab::Kubernetes::Errors::AUTHENTICATION
+ { status: :authentication_failure }
+ rescue Kubeclient::HttpError => e
+ { status: kubeclient_error_status(e.message) }
+ rescue => e
+ Gitlab::ErrorTracking.track_exception(e, cluster_id: cluster_id)
+
+ { status: :unknown_failure }
+ end
+
+ # KubeClient uses the same error class
+ # For connection errors (eg. timeout) and
+ # for Kubernetes errors.
+ def self.kubeclient_error_status(message)
+ if message&.match?(/timed out|timeout/i)
+ :unreachable
+ else
+ :authentication_failure
+ end
+ end
+
# We disable redirects through 'http_max_redirects: 0',
# so that KubeClient does not follow redirects and
# expose internal services.
@@ -125,19 +157,11 @@ module Gitlab
end
def create_or_update_cluster_role_binding(resource)
- if cluster_role_binding_exists?(resource)
- update_cluster_role_binding(resource)
- else
- create_cluster_role_binding(resource)
- end
+ update_cluster_role_binding(resource)
end
def create_or_update_role_binding(resource)
- if role_binding_exists?(resource)
- update_role_binding(resource)
- else
- create_role_binding(resource)
- end
+ update_role_binding(resource)
end
def create_or_update_service_account(resource)
@@ -164,18 +188,6 @@ module Gitlab
Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false)
end
- def cluster_role_binding_exists?(resource)
- get_cluster_role_binding(resource.metadata.name)
- rescue ::Kubeclient::ResourceNotFoundError
- false
- end
-
- def role_binding_exists?(resource)
- get_role_binding(resource.metadata.name, resource.metadata.namespace)
- rescue ::Kubeclient::ResourceNotFoundError
- false
- end
-
def service_account_exists?(resource)
get_service_account(resource.metadata.name, resource.metadata.namespace)
rescue ::Kubeclient::ResourceNotFoundError
diff --git a/lib/gitlab/kubernetes/network_policy.rb b/lib/gitlab/kubernetes/network_policy.rb
new file mode 100644
index 00000000000..ea25d81cbd2
--- /dev/null
+++ b/lib/gitlab/kubernetes/network_policy.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class NetworkPolicy
+ def initialize(name:, namespace:, pod_selector:, ingress:, creation_timestamp: nil, policy_types: ["Ingress"], egress: nil)
+ @name = name
+ @namespace = namespace
+ @creation_timestamp = creation_timestamp
+ @pod_selector = pod_selector
+ @policy_types = policy_types
+ @ingress = ingress
+ @egress = egress
+ end
+
+ def self.from_yaml(manifest)
+ return unless manifest
+
+ policy = YAML.safe_load(manifest, symbolize_names: true)
+ return if !policy[:metadata] || !policy[:spec]
+
+ metadata = policy[:metadata]
+ spec = policy[:spec]
+ self.new(
+ name: metadata[:name],
+ namespace: metadata[:namespace],
+ pod_selector: spec[:podSelector],
+ policy_types: spec[:policyTypes],
+ ingress: spec[:ingress],
+ egress: spec[:egress]
+ )
+ rescue Psych::SyntaxError, Psych::DisallowedClass
+ nil
+ end
+
+ def self.from_resource(resource)
+ return unless resource
+ return if !resource[:metadata] || !resource[:spec]
+
+ metadata = resource[:metadata]
+ spec = resource[:spec].to_h
+ self.new(
+ name: metadata[:name],
+ namespace: metadata[:namespace],
+ creation_timestamp: metadata[:creationTimestamp],
+ pod_selector: spec[:podSelector],
+ policy_types: spec[:policyTypes],
+ ingress: spec[:ingress],
+ egress: spec[:egress]
+ )
+ end
+
+ def generate
+ ::Kubeclient::Resource.new.tap do |resource|
+ resource.metadata = metadata
+ resource.spec = spec
+ end
+ end
+
+ def as_json(opts = nil)
+ {
+ name: name,
+ namespace: namespace,
+ creation_timestamp: creation_timestamp,
+ manifest: manifest
+ }
+ end
+
+ private
+
+ attr_reader :name, :namespace, :creation_timestamp, :pod_selector, :policy_types, :ingress, :egress
+
+ def metadata
+ { name: name, namespace: namespace }
+ end
+
+ def spec
+ {
+ podSelector: pod_selector,
+ policyTypes: policy_types,
+ ingress: ingress,
+ egress: egress
+ }
+ end
+
+ def manifest
+ YAML.dump({ metadata: metadata, spec: spec }.deep_stringify_keys)
+ end
+ end
+ end
+end