class HubClustersCreator::Providers::GKE

GKE provides the GKE implmentation

Constants

Compute
Container
DEFAULT_PSP_CLUSTERROLE_BINDING
DEFAULT_PSP_CLUSTER_ROLE
Dns

Public Class Methods

new(provider) click to toggle source
# File lib/hub-clusters-creator/providers/gke/gke.rb, line 78
def initialize(provider)
  @account = provider[:account]
  @project = provider[:project]
  @region = provider[:region]
  @compute = Compute::ComputeService.new
  @gke = Container::ContainerService.new
  @dns = Dns::DnsService.new
  @client = nil

  @compute.authorization = authorize
  @gke.authorization = authorize
  @dns.authorization = authorize
end

Public Instance Methods

create(name, config) click to toggle source

create is responsible for building the infrastructure

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 93
def create(name, config)
  # @step: validate the configuration
  begin
    validate(config)
  rescue StandardError => e
    raise ConfigurationError, "invalid configuration, error: #{e}"
  end

  # @step: provision the infrastructure
  begin
    provision_gke(name, config)
  rescue StandardError => e
    raise InfrastructureError, "failed to provision cluster: '#{name}', error: #{e}"
  end

  # @step: initialize the cluster
  begin
    c = provision_cluster(name, config)
  rescue StandardError => e
    raise InitializerError, "failed to initialize the cluster: '#{name}', error: #{e}"
  end

  {
    cluster: {
      ca: c.master_auth.cluster_ca_certificate,
      endpoint: "https://#{c.endpoint}",
      token: @client.account('sysadmin')
    },
    config: config,
    services: {
      grafana: {
        hostname: config[:grafana_hostname]
      }
    }
  }
end
destroy(name) click to toggle source

destroy is used to kill off a cluster

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 131
def destroy(name)
  @gke.delete_project_location_cluster("projects/#{@project}/locations/#{@region}/clusters/#{name}")
end

Private Instance Methods

authorize(scopes = ['https://www.googleapis.com/auth/cloud-platform']) click to toggle source

authorize is responsible for providing an access token to operate

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 251
def authorize(scopes = ['https://www.googleapis.com/auth/cloud-platform'])
  if @authorizer.nil?
    @authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: StringIO.new(@account),
      scope: scopes
    )
    @authorizer.fetch_access_token!
  end
  @authorizer
end
default_cloud_nat(name = 'cloud-nat') click to toggle source

default_cloud_nat returns a default cloud nat configuration

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 239
def default_cloud_nat(name = 'cloud-nat')
  [
    Google::Apis::ComputeV1::RouterNat.new(
      log_config: Google::Apis::ComputeV1::RouterNatLogConfig.new(enable: false, filter: 'ALL'),
      name: name,
      nat_ip_allocate_option: 'AUTO_ONLY',
      source_subnetwork_ip_ranges_to_nat: 'ALL_SUBNETWORKS_ALL_IP_RANGES'
    )
  ]
end
patch_router(name, router) click to toggle source

patch_router is a wrapper for the patch router

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 234
def patch_router(name, router)
  @compute.patch_router(@project, @region, name, router)
end
provision_cluster(name, config) click to toggle source

provision_cluster is responsible for kickstarting the cluster rubocop:disable Metrics/AbcSize

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 175
def provision_cluster(name, config)
  info "waiting for the master api endpoint to be available on cluster: #{name}"
  thing = cluster(name)
  @client = HubClustersCreator::Kube.new(thing.endpoint, token: authorize.access_token)
  @client.wait_for_kubeapi

  # @step: if psp is enabled we need to add the roles and bindings
  info 'creating the default psp binding to unpriviledged policy'
  @client.kubectl(DEFAULT_PSP_CLUSTER_ROLE)
  @client.kubectl(DEFAULT_PSP_CLUSTERROLE_BINDING)

  # @step: bootstrap the cluster and wait
  HubClustersCreator::Providers::Bootstrap.new(name, @client, config).bootstrap

  ingress = @client.get('loki-grafana', 'loki', 'ingresses', version: 'extensions/v1beta1')
  address = ingress.status.loadBalancer.ingress.first.ip

  # @step: update the dns record for the ingress
  unless (config[:grafana_hostname] || '').empty?
    info "adding a dns record for #{config[:grafana_hostname]} => #{address}"
    dns(config[:grafana_hostname].split('.').first, address, config[:domain])
  end

  cluster(name)
end
provision_gke(name, config) click to toggle source

provision_gke is responsible for provisioning the infrastucture rubocop:disable Metrics/AbcSize

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 139
def provision_gke(name, config)
  info "checking if the gke cluster: '#{name}' exists"
  if cluster?(name)
    info "skipping the creation of cluster: '#{name}' as it already exists"
  else
    info "cluster: '#{name}' does not exist, creating now"
    path = "projects/#{@project}/locations/#{@region}"
    operation = @gke.create_project_location_cluster(path, cluster_spec(config))

    info "waiting for the cluster: '#{name}' to be created, operation: '#{operation.name}'"
    status = hold_for_operation(operation.name)
    unless status.status_message.nil?
      raise InfrastructureError, "operation: '#{x.operation_type}' failed, error: #{x.status_message}"
    end
  end
  gke = cluster(name)

  # @step: create a cloud-nat device if private networking enabled
  # and nothing exists already
  if config[:enable_private_network]
    info 'checking if cloud-nat device has been created'
    router('router') do |x|
      unless x.nats
        x.nats = default_cloud_nat('cloud-nat')
        patch_router('router', x)
      end
    end
  end

  info "provisioning a dns entry for the master api = > #{gke.endpoint}"
  # dns(kubeapi_name(config).to_s, gke.endpoint, config[:domain])
end
validate(config) click to toggle source

validate is responsible for validating the options for cluster creation rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity

# File lib/hub-clusters-creator/providers/gke/gke.rb, line 204
def validate(config)
  raise ConfigurationError, "domain: #{config[:domain]} does not exist within project" unless domain?(config[:domain])
  raise ConfigurationError, 'disk size must be positive' unless config[:disk_size_gb].positive?
  raise ConfigurationError, 'size must be positive' unless config[:size].positive?

  # @check the networking options
  raise ConfigurationError, 'the network does not exist' unless network?(config[:network])
  raise ConfigurationError, 'the subnetwork does not exist' unless subnet?(config[:subnetwork], config[:network]) && !config[:create_subnetwork]

  # @check if subnets exist - need to do something more clever
  # and check for overlapping subnety really but i can't find a gem
  network_checks = []
  network_checks.push(config['cluster_ipv4_cidr']) if config['cluster_ipv4_cidr']
  network_checks.push(config['master_ipv4_cidr_block']) if config['master_ipv4_cidr_block']
  network_checks.push(config['services_ipv4_cidr']) if config['services_ipv4_cidr']

  nets = networks
  network_checks.each do |n|
    nets.each { |x| raise ConfigurationError, "network: #{n} already exists" if n == x.cidr }
  end

  if config[:enable_private_network] && !config[:master_ipv4_cidr_block]
    raise ConfigurationError, 'you must specify a master_ipv4_cidr_block'
  end

  config
end