Commit 2d1a77b8 authored by Shinya Maeda's avatar Shinya Maeda

Revert KubernetesService. Introduce FetchKubernetesTokenService.

parent e499c1c3
...@@ -33,13 +33,15 @@ module Ci ...@@ -33,13 +33,15 @@ module Ci
} }
def error!(reason) def error!(reason)
update!(status: statuses[:errored], update!(status: statuses[:errored], status_reason: reason, gcp_token: nil)
status_reason: reason,
gcp_token: nil)
end end
def on_creation? def on_creation?
scheduled? || creating? scheduled? || creating?
end end
def api_url
'https://' + endpoint
end
end end
end end
...@@ -15,7 +15,6 @@ class KubernetesService < DeploymentService ...@@ -15,7 +15,6 @@ class KubernetesService < DeploymentService
# Bearer authentication # Bearer authentication
# TODO: user/password auth, client certificates # TODO: user/password auth, client certificates
prop_accessor :token prop_accessor :token
attr_accessor :username, :password
# Provide a custom CA bundle for self-signed deployments # Provide a custom CA bundle for self-signed deployments
prop_accessor :ca_pem prop_accessor :ca_pem
...@@ -139,15 +138,6 @@ class KubernetesService < DeploymentService ...@@ -139,15 +138,6 @@ class KubernetesService < DeploymentService
TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze
def read_secrets
kubeclient = build_kubeclient!
kubeclient.get_secrets.as_json
rescue KubeException => err
raise err unless err.error_code == 404
[]
end
private private
def kubeconfig def kubeconfig
...@@ -167,7 +157,7 @@ class KubernetesService < DeploymentService ...@@ -167,7 +157,7 @@ class KubernetesService < DeploymentService
end end
def build_kubeclient!(api_path: 'api', api_version: 'v1') def build_kubeclient!(api_path: 'api', api_version: 'v1')
raise "Incomplete settings" unless api_url && (token || (username && password)) raise "Incomplete settings" unless api_url && actual_namespace && token
::Kubeclient::Client.new( ::Kubeclient::Client.new(
join_api_url(api_path), join_api_url(api_path),
...@@ -200,11 +190,7 @@ class KubernetesService < DeploymentService ...@@ -200,11 +190,7 @@ class KubernetesService < DeploymentService
end end
def kubeclient_auth_options def kubeclient_auth_options
if token { bearer_token: token }
{ bearer_token: token }
else
{ username: username, password: password }
end
end end
def join_api_url(api_path) def join_api_url(api_path)
......
module Ci module Ci
class CreateClusterService < BaseService class CreateClusterService < BaseService
def execute(access_token) def execute(access_token)
if params['machine_type'].blank?
params['machine_type'] = GoogleApi::CloudPlatform::Client::DEFAULT_MACHINE_TYPE
end
project.clusters.create( project.clusters.create(
params.merge(user: current_user, params.merge(user: current_user,
status: Ci::Cluster.statuses[:scheduled], status: Ci::Cluster.statuses[:scheduled],
......
##
# TODO:
# Almost components in this class were copied from app/models/project_services/kubernetes_service.rb
# We should dry up those classes not to repeat the same code.
# Maybe we should have a special facility (e.g. lib/kubernetes_api) to maintain all Kubernetes API caller.
module Ci
class FetchKubernetesTokenService
attr_reader :api_url, :ca_pem, :username, :password
def initialize(api_url, ca_pem, username, password)
@api_url = api_url
@ca_pem = ca_pem
@username = username
@password = password
end
def execute
read_secrets.each do |secret|
name = secret.dig('metadata', 'name')
if /default-token/ =~ name
token_base64 = secret.dig('data', 'token')
return Base64.decode64(token_base64) if token_base64
end
end
end
private
def read_secrets
kubeclient = build_kubeclient!
kubeclient.get_secrets.as_json
rescue KubeException => err
raise err unless err.error_code == 404
[]
end
def build_kubeclient!(api_path: 'api', api_version: 'v1')
raise "Incomplete settings" unless api_url && username && password
::Kubeclient::Client.new(
join_api_url(api_path),
api_version,
auth_options: { username: username, password: password },
ssl_options: kubeclient_ssl_options,
http_proxy_uri: ENV['http_proxy']
)
end
def join_api_url(api_path)
url = URI.parse(api_url)
prefix = url.path.sub(%r{/+\z}, '')
url.path = [prefix, api_path].join("/")
url.to_s
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
end
end
module Ci module Ci
class IntegrateClusterService class IntegrateClusterService
def execute(cluster, endpoint, ca_cert, token, username, password) def execute(cluster, endpoint, ca_cert, token, username, password)
kubernetes_service ||= cluster.project.find_or_initialize_service('kubernetes')
Ci::Cluster.transaction do Ci::Cluster.transaction do
# Update service kubernetes_service ||=
kubernetes_service.attributes = { cluster.project.find_or_initialize_service('kubernetes')
active: true,
api_url: endpoint,
ca_pem: ca_cert,
namespace: cluster.project_namespace,
token: token
}
kubernetes_service.save!
# Save info in cluster record
cluster.update!( cluster.update!(
enabled: true, enabled: true,
service: kubernetes_service, service: kubernetes_service,
...@@ -25,10 +14,16 @@ module Ci ...@@ -25,10 +14,16 @@ module Ci
ca_cert: ca_cert, ca_cert: ca_cert,
endpoint: endpoint, endpoint: endpoint,
gcp_token: nil, gcp_token: nil,
status: Ci::Cluster.statuses[:created] gcp_operation_id: nil,
) status: Ci::Cluster.statuses[:created])
end
kubernetes_service.update!(
active: true,
api_url: cluster.api_url,
ca_pem: ca_cert,
namespace: cluster.project_namespace,
token: token)
end
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
cluster.error!("Failed to integrate cluster into kubernetes_service: #{e.message}") cluster.error!("Failed to integrate cluster into kubernetes_service: #{e.message}")
end end
......
...@@ -2,22 +2,18 @@ module Ci ...@@ -2,22 +2,18 @@ module Ci
class UpdateClusterService < BaseService class UpdateClusterService < BaseService
def execute(cluster) def execute(cluster)
Ci::Cluster.transaction do Ci::Cluster.transaction do
if params['enabled'] == 'true' cluster.update!(enabled: params['enabled'])
cluster.service.attributes = { if params['enabled'] == 'true'
cluster.service.update!(
active: true, active: true,
api_url: cluster.endpoint, api_url: cluster.api_url,
ca_pem: cluster.ca_cert, ca_pem: cluster.ca_cert,
namespace: cluster.project_namespace, namespace: cluster.project_namespace,
token: cluster.kubernetes_token token: cluster.kubernetes_token)
}
cluster.service.save!
else else
cluster.service.update(active: false) cluster.service.update(active: false)
end end
cluster.update!(enabled: params['enabled'])
end end
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
cluster.errors.add(:base, e.message) cluster.errors.add(:base, e.message)
......
...@@ -33,4 +33,4 @@ ...@@ -33,4 +33,4 @@
= field.submit s_('ClusterIntegration|Create cluster'), class: 'btn btn-save' = field.submit s_('ClusterIntegration|Create cluster'), class: 'btn btn-save'
-# TODO: Remove before merge -# TODO: Remove before merge
= link_to "Create on Google Container Engine", namespace_project_clusters_path(@project.namespace, @project, cluster: {cluster_name: "gke-test-creation#{Random.rand(100)}", gcp_project_id: 'gitlab-internal-153318', cluster_zone: 'us-central1-a', cluster_size: '1', project_namespace: 'aaa', machine_type: '???'}), method: :post = link_to "Create on Google Container Engine", namespace_project_clusters_path(@project.namespace, @project, cluster: {cluster_name: "gke-test-creation#{Random.rand(100)}", gcp_project_id: 'gitlab-internal-153318', cluster_zone: 'us-central1-a', cluster_size: '1', project_namespace: 'aaa', machine_type: 'n1-standard-1'}), method: :post
\ No newline at end of file \ No newline at end of file
...@@ -17,69 +17,56 @@ class WaitForClusterCreationWorker ...@@ -17,69 +17,56 @@ class WaitForClusterCreationWorker
GoogleApi::CloudPlatform::Client.new(cluster.gcp_token, nil) GoogleApi::CloudPlatform::Client.new(cluster.gcp_token, nil)
operation = api_client.projects_zones_operations( operation = api_client.projects_zones_operations(
cluster.gcp_project_id, cluster.gcp_project_id,
cluster.cluster_zone, cluster.cluster_zone,
cluster.gcp_operation_id cluster.gcp_operation_id)
)
if operation.is_a?(StandardError) if operation.is_a?(StandardError)
return cluster.error!("Failed to request to CloudPlatform; #{operation.message}") return cluster.error!("Failed to request to CloudPlatform; #{operation.message}")
end end
case operation.status case operation.status
when 'DONE'
integrate(api_client, cluster)
when 'RUNNING' when 'RUNNING'
if Time.now < operation.start_time.to_time + TIMEOUT if Time.now < operation.start_time.to_time + TIMEOUT
WaitForClusterCreationWorker.perform_in(EAGER_INTERVAL, cluster.id) WaitForClusterCreationWorker.perform_in(EAGER_INTERVAL, cluster.id)
else else
return cluster.error!("Cluster creation time exceeds timeout; #{TIMEOUT}") return cluster.error!("Cluster creation time exceeds timeout; #{TIMEOUT}")
end end
when 'DONE'
integrate(cluster, api_client)
else else
return cluster.error!("Unexpected operation status; #{operation.status_message}") return cluster.error!("Unexpected operation status; #{operation.status} #{operation.status_message}")
end end
end end
def integrate(api_client, cluster) def integrate(cluster, api_client)
# Get cluster details (end point, etc)
gke_cluster = api_client.projects_zones_clusters_get( gke_cluster = api_client.projects_zones_clusters_get(
cluster.gcp_project_id, cluster.gcp_project_id,
cluster.cluster_zone, cluster.cluster_zone,
cluster.cluster_name cluster.cluster_name)
)
if gke_cluster.is_a?(StandardError) if gke_cluster.is_a?(StandardError)
return cluster.error!("Failed to request to CloudPlatform; #{gke_cluster.message}") return cluster.error!("Failed to request to CloudPlatform; #{gke_cluster.message}")
end end
# Get k8s token begin
kubernetes_token = '' endpoint = gke_cluster.endpoint
KubernetesService.new.tap do |ks| api_url = 'https://' + endpoint
ks.api_url = 'https://' + gke_cluster.endpoint ca_cert = Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate)
ks.ca_pem = Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate) username = gke_cluster.master_auth.username
ks.username = gke_cluster.master_auth.username password = gke_cluster.master_auth.password
ks.password = gke_cluster.master_auth.password rescue Exception => e
secrets = ks.read_secrets return cluster.error!("Can not extract the extected data; #{e}")
secrets.each do |secret|
name = secret.dig('metadata', 'name')
if /default-token/ =~ name
token_base64 = secret.dig('data', 'token')
kubernetes_token = Base64.decode64(token_base64)
break
end
end
end end
kubernetes_token = Ci::FetchKubernetesTokenService.new(
api_url, ca_cert, username, password).execute
unless kubernetes_token unless kubernetes_token
return cluster.error!('Failed to get a default token of kubernetes') return cluster.error!('Failed to get a default token of kubernetes')
end end
# k8s endpoint, ca_cert Ci::IntegrateClusterService.new.execute(
endpoint = 'https://' + gke_cluster.endpoint cluster, endpoint, ca_cert, kubernetes_token, username, password)
ca_cert = Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate)
username = gke_cluster.master_auth.username
password = gke_cluster.master_auth.password
Ci::IntegrateClusterService.new.execute(cluster, endpoint, ca_cert, kubernetes_token, username, password)
end end
end end
...@@ -3,6 +3,8 @@ require 'google/apis/container_v1' ...@@ -3,6 +3,8 @@ require 'google/apis/container_v1'
module GoogleApi module GoogleApi
module CloudPlatform module CloudPlatform
class Client < GoogleApi::Auth class Client < GoogleApi::Auth
DEFAULT_MACHINE_TYPE = 'n1-standard-1'
class << self class << self
def session_key_for_token def session_key_for_token
:cloud_platform_access_token :cloud_platform_access_token
...@@ -27,8 +29,6 @@ module GoogleApi ...@@ -27,8 +29,6 @@ module GoogleApi
cluster cluster
end end
# Responce exmaple
# TODO: machine_type : Defailt 3.75 GB
def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:) def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:)
service = Google::Apis::ContainerV1::ContainerService.new service = Google::Apis::ContainerV1::ContainerService.new
service.authorization = access_token service.authorization = access_token
...@@ -37,7 +37,10 @@ module GoogleApi ...@@ -37,7 +37,10 @@ module GoogleApi
{ {
"cluster": { "cluster": {
"name": cluster_name, "name": cluster_name,
"initial_node_count": cluster_size "initial_node_count": cluster_size,
"node_config": {
"machine_type": machine_type # Default 3.75 GB, if ommit
}
} }
} }
) )
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment