Commit 98dbb0a4 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 9ce26d3d
...@@ -41,6 +41,8 @@ export default class Clusters { ...@@ -41,6 +41,8 @@ export default class Clusters {
managePrometheusPath, managePrometheusPath,
clusterEnvironmentsPath, clusterEnvironmentsPath,
hasRbac, hasRbac,
providerType,
preInstalledKnative,
clusterType, clusterType,
clusterStatus, clusterStatus,
clusterStatusReason, clusterStatusReason,
...@@ -50,6 +52,7 @@ export default class Clusters { ...@@ -50,6 +52,7 @@ export default class Clusters {
environmentsHelpPath, environmentsHelpPath,
clustersHelpPath, clustersHelpPath,
deployBoardsHelpPath, deployBoardsHelpPath,
cloudRunHelpPath,
clusterId, clusterId,
} = document.querySelector('.js-edit-cluster-form').dataset; } = document.querySelector('.js-edit-cluster-form').dataset;
...@@ -65,10 +68,13 @@ export default class Clusters { ...@@ -65,10 +68,13 @@ export default class Clusters {
environmentsHelpPath, environmentsHelpPath,
clustersHelpPath, clustersHelpPath,
deployBoardsHelpPath, deployBoardsHelpPath,
cloudRunHelpPath,
); );
this.store.setManagePrometheusPath(managePrometheusPath); this.store.setManagePrometheusPath(managePrometheusPath);
this.store.updateStatus(clusterStatus); this.store.updateStatus(clusterStatus);
this.store.updateStatusReason(clusterStatusReason); this.store.updateStatusReason(clusterStatusReason);
this.store.updateProviderType(providerType);
this.store.updatePreInstalledKnative(preInstalledKnative);
this.store.updateRbac(hasRbac); this.store.updateRbac(hasRbac);
this.service = new ClustersService({ this.service = new ClustersService({
endpoint: statusPath, endpoint: statusPath,
...@@ -153,6 +159,9 @@ export default class Clusters { ...@@ -153,6 +159,9 @@ export default class Clusters {
ingressHelpPath: this.state.ingressHelpPath, ingressHelpPath: this.state.ingressHelpPath,
managePrometheusPath: this.state.managePrometheusPath, managePrometheusPath: this.state.managePrometheusPath,
ingressDnsHelpPath: this.state.ingressDnsHelpPath, ingressDnsHelpPath: this.state.ingressDnsHelpPath,
cloudRunHelpPath: this.state.cloudRunHelpPath,
providerType: this.state.providerType,
preInstalledKnative: this.state.preInstalledKnative,
rbac: this.state.rbac, rbac: this.state.rbac,
}, },
}); });
......
...@@ -78,6 +78,10 @@ export default { ...@@ -78,6 +78,10 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
installedVia: {
type: String,
required: false,
},
version: { version: {
type: String, type: String,
required: false, required: false,
...@@ -311,6 +315,11 @@ export default { ...@@ -311,6 +315,11 @@ export default {
> >
<span v-else class="js-cluster-application-title">{{ title }}</span> <span v-else class="js-cluster-application-title">{{ title }}</span>
</strong> </strong>
<span
v-if="installedVia"
class="js-cluster-application-installed-via"
v-html="installedVia"
></span>
<slot name="description"></slot> <slot name="description"></slot>
<div v-if="hasError" class="cluster-application-error text-danger prepend-top-10"> <div v-if="hasError" class="cluster-application-error text-danger prepend-top-10">
<p class="js-cluster-application-general-error-message append-bottom-0"> <p class="js-cluster-application-general-error-message append-bottom-0">
......
...@@ -16,7 +16,7 @@ import { s__, sprintf } from '../../locale'; ...@@ -16,7 +16,7 @@ import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue'; import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import KnativeDomainEditor from './knative_domain_editor.vue'; import KnativeDomainEditor from './knative_domain_editor.vue';
import { CLUSTER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants'; import { CLUSTER_TYPE, PROVIDER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import eventHub from '~/clusters/event_hub'; import eventHub from '~/clusters/event_hub';
...@@ -54,11 +54,26 @@ export default { ...@@ -54,11 +54,26 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
cloudRunHelpPath: {
type: String,
required: false,
default: '',
},
managePrometheusPath: { managePrometheusPath: {
type: String, type: String,
required: false, required: false,
default: '', default: '',
}, },
providerType: {
type: String,
required: false,
default: '',
},
preInstalledKnative: {
type: Boolean,
required: false,
default: false,
},
rbac: { rbac: {
type: Boolean, type: Boolean,
required: false, required: false,
...@@ -156,6 +171,25 @@ export default { ...@@ -156,6 +171,25 @@ export default {
knative() { knative() {
return this.applications.knative; return this.applications.knative;
}, },
cloudRun() {
return this.providerType === PROVIDER_TYPE.GCP && this.preInstalledKnative;
},
installedVia() {
if (this.cloudRun) {
return sprintf(
_.escape(s__(`ClusterIntegration|installed via %{installed_via}`)),
{
installed_via: `<a href="${
this.cloudRunHelpPath
}" target="_blank" rel="noopener noreferrer">${_.escape(
s__('ClusterIntegration|Cloud Run'),
)}</a>`,
},
false,
);
}
return null;
},
}, },
created() { created() {
this.helmInstallIllustration = helmInstallIllustration; this.helmInstallIllustration = helmInstallIllustration;
...@@ -468,6 +502,7 @@ export default { ...@@ -468,6 +502,7 @@ export default {
:installed="applications.knative.installed" :installed="applications.knative.installed"
:install-failed="applications.knative.installFailed" :install-failed="applications.knative.installFailed"
:install-application-request-params="{ hostname: applications.knative.hostname }" :install-application-request-params="{ hostname: applications.knative.hostname }"
:installed-via="installedVia"
:uninstallable="applications.knative.uninstallable" :uninstallable="applications.knative.uninstallable"
:uninstall-successful="applications.knative.uninstallSuccessful" :uninstall-successful="applications.knative.uninstallSuccessful"
:uninstall-failed="applications.knative.uninstallFailed" :uninstall-failed="applications.knative.uninstallFailed"
...@@ -499,7 +534,7 @@ export default { ...@@ -499,7 +534,7 @@ export default {
</p> </p>
<knative-domain-editor <knative-domain-editor
v-if="knative.installed || (helmInstalled && rbac)" v-if="(knative.installed || (helmInstalled && rbac)) && !preInstalledKnative"
:knative="knative" :knative="knative"
:ingress-dns-help-path="ingressDnsHelpPath" :ingress-dns-help-path="ingressDnsHelpPath"
@save="saveKnativeDomain" @save="saveKnativeDomain"
......
...@@ -5,6 +5,11 @@ export const CLUSTER_TYPE = { ...@@ -5,6 +5,11 @@ export const CLUSTER_TYPE = {
PROJECT: 'project_type', PROJECT: 'project_type',
}; };
// These need to match the available providers in app/models/clusters/providers/
export const PROVIDER_TYPE = {
GCP: 'gcp',
};
// These need to match what is returned from the server // These need to match what is returned from the server
export const APPLICATION_STATUS = { export const APPLICATION_STATUS = {
NO_STATUS: null, NO_STATUS: null,
...@@ -19,6 +24,7 @@ export const APPLICATION_STATUS = { ...@@ -19,6 +24,7 @@ export const APPLICATION_STATUS = {
UNINSTALLING: 'uninstalling', UNINSTALLING: 'uninstalling',
UNINSTALL_ERRORED: 'uninstall_errored', UNINSTALL_ERRORED: 'uninstall_errored',
ERROR: 'errored', ERROR: 'errored',
PRE_INSTALLED: 'pre_installed',
}; };
/* /*
...@@ -29,6 +35,7 @@ export const APPLICATION_INSTALLED_STATUSES = [ ...@@ -29,6 +35,7 @@ export const APPLICATION_INSTALLED_STATUSES = [
APPLICATION_STATUS.INSTALLED, APPLICATION_STATUS.INSTALLED,
APPLICATION_STATUS.UPDATING, APPLICATION_STATUS.UPDATING,
APPLICATION_STATUS.UNINSTALLING, APPLICATION_STATUS.UNINSTALLING,
APPLICATION_STATUS.PRE_INSTALLED,
]; ];
// These are only used client-side // These are only used client-side
......
...@@ -13,6 +13,7 @@ const { ...@@ -13,6 +13,7 @@ const {
UPDATE_ERRORED, UPDATE_ERRORED,
UNINSTALLING, UNINSTALLING,
UNINSTALL_ERRORED, UNINSTALL_ERRORED,
PRE_INSTALLED,
} = APPLICATION_STATUS; } = APPLICATION_STATUS;
const applicationStateMachine = { const applicationStateMachine = {
...@@ -63,6 +64,9 @@ const applicationStateMachine = { ...@@ -63,6 +64,9 @@ const applicationStateMachine = {
uninstallFailed: true, uninstallFailed: true,
}, },
}, },
[PRE_INSTALLED]: {
target: PRE_INSTALLED,
},
}, },
}, },
[NOT_INSTALLABLE]: { [NOT_INSTALLABLE]: {
...@@ -123,6 +127,27 @@ const applicationStateMachine = { ...@@ -123,6 +127,27 @@ const applicationStateMachine = {
}, },
}, },
}, },
[PRE_INSTALLED]: {
on: {
[UPDATE_EVENT]: {
target: UPDATING,
effects: {
updateFailed: false,
updateSuccessful: false,
},
},
[NOT_INSTALLABLE]: {
target: NOT_INSTALLABLE,
},
[UNINSTALL_EVENT]: {
target: UNINSTALLING,
effects: {
uninstallFailed: false,
uninstallSuccessful: false,
},
},
},
},
[UPDATING]: { [UPDATING]: {
on: { on: {
[UPDATED]: { [UPDATED]: {
......
...@@ -35,7 +35,10 @@ export default class ClusterStore { ...@@ -35,7 +35,10 @@ export default class ClusterStore {
environmentsHelpPath: null, environmentsHelpPath: null,
clustersHelpPath: null, clustersHelpPath: null,
deployBoardsHelpPath: null, deployBoardsHelpPath: null,
cloudRunHelpPath: null,
status: null, status: null,
providerType: null,
preInstalledKnative: false,
rbac: false, rbac: false,
statusReason: null, statusReason: null,
applications: { applications: {
...@@ -95,6 +98,7 @@ export default class ClusterStore { ...@@ -95,6 +98,7 @@ export default class ClusterStore {
environmentsHelpPath, environmentsHelpPath,
clustersHelpPath, clustersHelpPath,
deployBoardsHelpPath, deployBoardsHelpPath,
cloudRunHelpPath,
) { ) {
this.state.helpPath = helpPath; this.state.helpPath = helpPath;
this.state.ingressHelpPath = ingressHelpPath; this.state.ingressHelpPath = ingressHelpPath;
...@@ -102,6 +106,7 @@ export default class ClusterStore { ...@@ -102,6 +106,7 @@ export default class ClusterStore {
this.state.environmentsHelpPath = environmentsHelpPath; this.state.environmentsHelpPath = environmentsHelpPath;
this.state.clustersHelpPath = clustersHelpPath; this.state.clustersHelpPath = clustersHelpPath;
this.state.deployBoardsHelpPath = deployBoardsHelpPath; this.state.deployBoardsHelpPath = deployBoardsHelpPath;
this.state.cloudRunHelpPath = cloudRunHelpPath;
} }
setManagePrometheusPath(managePrometheusPath) { setManagePrometheusPath(managePrometheusPath) {
...@@ -112,6 +117,14 @@ export default class ClusterStore { ...@@ -112,6 +117,14 @@ export default class ClusterStore {
this.state.status = status; this.state.status = status;
} }
updateProviderType(providerType) {
this.state.providerType = providerType;
}
updatePreInstalledKnative(preInstalledKnative) {
this.state.preInstalledKnative = parseBoolean(preInstalledKnative);
}
updateRbac(rbac) { updateRbac(rbac) {
this.state.rbac = parseBoolean(rbac); this.state.rbac = parseBoolean(rbac);
} }
......
...@@ -170,6 +170,7 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -170,6 +170,7 @@ class Clusters::ClustersController < Clusters::BaseController
:zone, :zone,
:num_nodes, :num_nodes,
:machine_type, :machine_type,
:cloud_run,
:legacy_abac :legacy_abac
]).merge( ]).merge(
provider_type: :gcp, provider_type: :gcp,
......
...@@ -27,7 +27,7 @@ module Clusters ...@@ -27,7 +27,7 @@ module Clusters
def set_initial_status def set_initial_status
return unless not_installable? return unless not_installable?
self.status = 'installable' if cluster&.platform_kubernetes_active? self.status = status_states[:installable] if cluster&.platform_kubernetes_active?
end end
# It can only be uninstalled if there are no other applications installed # It can only be uninstalled if there are no other applications installed
......
...@@ -23,7 +23,7 @@ module Clusters ...@@ -23,7 +23,7 @@ module Clusters
return unless cluster&.application_ingress_available? return unless cluster&.application_ingress_available?
ingress = cluster.application_ingress ingress = cluster.application_ingress
self.status = 'installable' if ingress.external_ip_or_hostname? self.status = status_states[:installable] if ingress.external_ip_or_hostname?
end end
def chart def chart
......
...@@ -21,7 +21,7 @@ module Clusters ...@@ -21,7 +21,7 @@ module Clusters
return unless not_installable? return unless not_installable?
return unless verify_cluster? return unless verify_cluster?
self.status = 'installable' self.status = status_states[:installable]
end end
state_machine :status do state_machine :status do
...@@ -47,6 +47,10 @@ module Clusters ...@@ -47,6 +47,10 @@ module Clusters
{ "domain" => hostname }.to_yaml { "domain" => hostname }.to_yaml
end end
def allowed_to_uninstall?
!pre_installed?
end
def install_command def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new( Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name, name: name,
......
...@@ -194,6 +194,10 @@ module Clusters ...@@ -194,6 +194,10 @@ module Clusters
end end
end end
def knative_pre_installed?
provider&.knative_pre_installed?
end
private private
def instance_domain def instance_domain
......
...@@ -15,7 +15,7 @@ module Clusters ...@@ -15,7 +15,7 @@ module Clusters
def set_initial_status def set_initial_status
return unless not_installable? return unless not_installable?
self.status = 'installable' if cluster&.application_helm_available? self.status = status_states[:installable] if cluster&.application_helm_available?
end end
def can_uninstall? def can_uninstall?
......
...@@ -28,6 +28,13 @@ module Clusters ...@@ -28,6 +28,13 @@ module Clusters
state :uninstalling, value: 7 state :uninstalling, value: 7
state :uninstall_errored, value: 8 state :uninstall_errored, value: 8
# Used for applications that are pre-installed by the cluster,
# e.g. Knative in GCP Cloud Run enabled clusters
# Because we cannot upgrade or uninstall Knative in these clusters,
# we define only one simple state transition to enter the `pre_installed` state,
# and no exit transitions.
state :pre_installed, value: 9
event :make_scheduled do event :make_scheduled do
transition [:installable, :errored, :installed, :updated, :update_errored, :uninstall_errored] => :scheduled transition [:installable, :errored, :installed, :updated, :update_errored, :uninstall_errored] => :scheduled
end end
...@@ -41,6 +48,10 @@ module Clusters ...@@ -41,6 +48,10 @@ module Clusters
transition [:updating] => :updated transition [:updating] => :updated
end end
event :make_pre_installed do
transition any => :pre_installed
end
event :make_errored do event :make_errored do
transition any - [:updating, :uninstalling] => :errored transition any - [:updating, :uninstalling] => :errored
transition [:updating] => :update_errored transition [:updating] => :update_errored
...@@ -90,12 +101,18 @@ module Clusters ...@@ -90,12 +101,18 @@ module Clusters
end end
end end
def status_states
self.class.state_machines[:status].states.each_with_object({}) do |state, states|
states[state.name] = state.value
end
end
def updateable? def updateable?
installed? || updated? || update_errored? installed? || updated? || update_errored?
end end
def available? def available?
installed? || updated? pre_installed? || installed? || updated?
end end
def update_in_progress? def update_in_progress?
......
...@@ -10,6 +10,9 @@ module Clusters ...@@ -10,6 +10,9 @@ module Clusters
default_value_for :zone, 'us-central1-a' default_value_for :zone, 'us-central1-a'
default_value_for :num_nodes, 3 default_value_for :num_nodes, 3
default_value_for :machine_type, 'n1-standard-2' default_value_for :machine_type, 'n1-standard-2'
default_value_for :cloud_run, false
scope :cloud_run, -> { where(cloud_run: true) }
attr_encrypted :access_token, attr_encrypted :access_token,
mode: :per_attribute_iv, mode: :per_attribute_iv,
...@@ -77,6 +80,10 @@ module Clusters ...@@ -77,6 +80,10 @@ module Clusters
@api_client ||= GoogleApi::CloudPlatform::Client.new(access_token, nil) @api_client ||= GoogleApi::CloudPlatform::Client.new(access_token, nil)
end end
def knative_pre_installed?
cloud_run?
end
end end
end end
end end
...@@ -11,9 +11,11 @@ module Boards ...@@ -11,9 +11,11 @@ module Boards
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def metadata def metadata
issues = Issue.arel_table
keys = metadata_fields.keys keys = metadata_fields.keys
columns = metadata_fields.values_at(*keys).join(', ') # TODO: eliminate need for SQL literal fragment
results = Issue.where(id: fetch_issues.select('issues.id')).pluck(columns) columns = Arel.sql(metadata_fields.values_at(*keys).join(', '))
results = Issue.where(id: fetch_issues.select(issues[:id])).pluck(columns)
Hash[keys.zip(results.flatten)] Hash[keys.zip(results.flatten)]
end end
......
...@@ -11,6 +11,7 @@ module Clusters ...@@ -11,6 +11,7 @@ module Clusters
configure_provider configure_provider
create_gitlab_service_account! create_gitlab_service_account!
configure_kubernetes configure_kubernetes
configure_pre_installed_knative if provider.knative_pre_installed?
cluster.save! cluster.save!
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
log_service_error(e.class.name, provider.id, e.message) log_service_error(e.class.name, provider.id, e.message)
...@@ -48,6 +49,13 @@ module Clusters ...@@ -48,6 +49,13 @@ module Clusters
token: request_kubernetes_token) token: request_kubernetes_token)
end end
def configure_pre_installed_knative
knative = cluster.build_application_knative(
hostname: 'example.com'
)
knative.make_pre_installed!
end
def request_kubernetes_token def request_kubernetes_token
Clusters::Kubernetes::FetchKubernetesTokenService.new( Clusters::Kubernetes::FetchKubernetesTokenService.new(
kube_client, kube_client,
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module Clusters module Clusters
module Gcp module Gcp
class ProvisionService class ProvisionService
CLOUD_RUN_ADDONS = %i[http_load_balancing istio_config cloud_run_config].freeze
attr_reader :provider attr_reader :provider
def execute(provider) def execute(provider)
...@@ -22,13 +24,16 @@ module Clusters ...@@ -22,13 +24,16 @@ module Clusters
private private
def get_operation_id def get_operation_id
enable_addons = provider.cloud_run? ? CLOUD_RUN_ADDONS : []
operation = provider.api_client.projects_zones_clusters_create( operation = provider.api_client.projects_zones_clusters_create(
provider.gcp_project_id, provider.gcp_project_id,
provider.zone, provider.zone,
provider.cluster.name, provider.cluster.name,
provider.num_nodes, provider.num_nodes,
machine_type: provider.machine_type, machine_type: provider.machine_type,
legacy_abac: provider.legacy_abac legacy_abac: provider.legacy_abac,
enable_addons: enable_addons
) )
unless operation.status == 'PENDING' || operation.status == 'RUNNING' unless operation.status == 'PENDING' || operation.status == 'RUNNING'
......
...@@ -65,6 +65,13 @@ ...@@ -65,6 +65,13 @@
%p.form-text.text-muted %p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end } = s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
.form-group
= provider_gcp_field.check_box :cloud_run, { label: s_('ClusterIntegration|Enable Cloud Run on GKE (beta)'),
label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'cloud-run-on-gke'), target: '_blank'
.form-group .form-group
= field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'), = field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'),
label_class: 'label-bold' } label_class: 'label-bold' }
......
...@@ -23,12 +23,15 @@ ...@@ -23,12 +23,15 @@
cluster_type: @cluster.cluster_type, cluster_type: @cluster.cluster_type,
cluster_status: @cluster.status_name, cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason, cluster_status_reason: @cluster.status_reason,
provider_type: @cluster.provider_type,
pre_installed_knative: @cluster.knative_pre_installed? ? 'true': 'false',
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'), help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'), ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'),
ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'), ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'),
environments_help_path: help_page_path('ci/environments', anchor: 'defining-environments'), environments_help_path: help_page_path('ci/environments', anchor: 'defining-environments'),
clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'), clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'),
deploy_boards_help_path: help_page_path('user/project/deploy_boards.html', anchor: 'enabling-deploy-boards'), deploy_boards_help_path: help_page_path('user/project/deploy_boards.html', anchor: 'enabling-deploy-boards'),
cloud_run_help_path: help_page_path('user/project/clusters/index.md', anchor: 'cloud-run-on-gke'),
manage_prometheus_path: manage_prometheus_path, manage_prometheus_path: manage_prometheus_path,
cluster_id: @cluster.id } } cluster_id: @cluster.id } }
......
---
title: Enable Cloud Run on GKE cluster creation
merge_request: 16566
author:
type: added
# frozen_string_literal: true
#
# google-api-client >= 0.26.0 supports enabling CloudRun and Istio during
# cluster creation, but fog-google currently hard deps on '~> 0.23.0', which
# prevents us from upgrading. We are injecting these options as hashes below
# as a workaround until this is resolved.
#
# This can be removed once fog-google and google-api-client can be upgraded.
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/66630 for more details.
#
require 'google/apis/container_v1beta1'
Google::Apis::ContainerV1beta1::AddonsConfig::Representation.tap do |representation|
representation.hash :cloud_run_config, as: 'cloudRunConfig'
representation.hash :istio_config, as: 'istioConfig'
end
...@@ -117,3 +117,4 @@ ...@@ -117,3 +117,4 @@
- [jira_connect, 1] - [jira_connect, 1]
- [update_external_pull_requests, 3] - [update_external_pull_requests, 3]
- [refresh_license_compliance_checks, 2] - [refresh_license_compliance_checks, 2]
- [design_management_new_version, 1]
# frozen_string_literal: true
class AddCloudRunToClustersProvidersGcp < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:cluster_providers_gcp, :cloud_run, :boolean, default: false)
end
def down
remove_column(:cluster_providers_gcp, :cloud_run)
end
end
# frozen_string_literal: true
class AddIndexToClustersProvidersGcpOnCloudRun < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index(:cluster_providers_gcp, :cloud_run)
end
def down
remove_concurrent_index(:cluster_providers_gcp, :cloud_run)
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_09_18_104222) do ActiveRecord::Schema.define(version: 2019_09_19_162036) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm" enable_extension "pg_trgm"
...@@ -965,6 +965,8 @@ ActiveRecord::Schema.define(version: 2019_09_18_104222) do ...@@ -965,6 +965,8 @@ ActiveRecord::Schema.define(version: 2019_09_18_104222) do
t.text "encrypted_access_token" t.text "encrypted_access_token"
t.string "encrypted_access_token_iv" t.string "encrypted_access_token_iv"
t.boolean "legacy_abac", default: false, null: false t.boolean "legacy_abac", default: false, null: false
t.boolean "cloud_run", default: false, null: false
t.index ["cloud_run"], name: "index_cluster_providers_gcp_on_cloud_run"
t.index ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true t.index ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true
end end
......
...@@ -154,6 +154,7 @@ new Kubernetes cluster to your project: ...@@ -154,6 +154,7 @@ new Kubernetes cluster to your project:
- **Number of nodes** - Enter the number of nodes you wish the cluster to have. - **Number of nodes** - Enter the number of nodes you wish the cluster to have.
- **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types) - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
of the Virtual Machine instance that the cluster will be based on. of the Virtual Machine instance that the cluster will be based on.
- **Enable Cloud Run on GKE (beta)** - Check this if you want to use Cloud Run on GKE for this cluster. See the [Cloud Run on GKE section](#cloud-run-on-gke) for more information.
- **GitLab-managed cluster** - Leave this checked if you want GitLab to manage namespaces and service accounts for this cluster. See the [Managed clusters section](#gitlab-managed-clusters) for more information. - **GitLab-managed cluster** - Leave this checked if you want GitLab to manage namespaces and service accounts for this cluster. See the [Managed clusters section](#gitlab-managed-clusters) for more information.
1. Finally, click the **Create Kubernetes cluster** button. 1. Finally, click the **Create Kubernetes cluster** button.
...@@ -339,6 +340,15 @@ functionalities needed to successfully build and deploy a containerized ...@@ -339,6 +340,15 @@ functionalities needed to successfully build and deploy a containerized
application. Bear in mind that the same credentials are used for all the application. Bear in mind that the same credentials are used for all the
applications running on the cluster. applications running on the cluster.
### Cloud Run on GKE
> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/16566) in GitLab 12.4.
You can choose to use Cloud Run on GKE in place of installing Knative and Istio
separately after the cluster has been created. This means that Cloud Run
(Knative), Istio, and HTTP Load Balancing will be enabled on the cluster at
create time and cannot be [installed or uninstalled](../../clusters/applications.md) separately.
### GitLab-managed clusters ### GitLab-managed clusters
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/22011) in GitLab 11.5. > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/22011) in GitLab 11.5.
......
# frozen_string_literal: true
module Gitlab
module Utils
module InlineHash
extend self
# Transforms a Hash into an inline Hash by merging its nested keys.
#
# Input
#
# {
# 'root_param' => 'Root',
# nested_param: {
# key: 'Value'
# },
# 'very' => {
# 'deep' => {
# 'nested' => {
# 'param' => 'Deep nested value'
# }
# }
# }
# }
#
#
# Result
#
# {
# 'root_param' => 'Root',
# 'nested_param.key' => 'Value',
# 'very.deep.nested.param' => 'Deep nested value'
# }
#
def merge_keys(hash, prefix: nil, connector: '.')
result = {}
base_prefix = prefix ? "#{prefix}#{connector}" : ''
pairs = hash.map { |key, value| ["#{base_prefix}#{key}", value] }
until pairs.empty?
key, value = pairs.shift
if value.is_a?(Hash)
value.each { |k, v| pairs.unshift ["#{key}#{connector}#{k}", v] }
else
result[key] = value
end
end
result
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Utils
class SafeInlineHash
# Validates the hash size using `Gitlab::Utils::DeepSize` before merging keys using `Gitlab::Utils::InlineHash`
def initialize(hash, prefix: nil, connector: '.')
@hash = hash
end
def self.merge_keys!(hash, prefix: nil, connector: '.')
new(hash).merge_keys!(prefix: prefix, connector: connector)
end
def merge_keys!(prefix:, connector:)
raise ArgumentError, 'The Hash is too big' unless valid?
Gitlab::Utils::InlineHash.merge_keys(hash, prefix: prefix, connector: connector)
end
private
attr_reader :hash
def valid?
Gitlab::Utils::DeepSize.new(hash).valid?
end
end
end
end
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
require 'google/apis/compute_v1' require 'google/apis/compute_v1'
require 'google/apis/container_v1' require 'google/apis/container_v1'
require 'google/apis/container_v1beta1'
require 'google/apis/cloudbilling_v1' require 'google/apis/cloudbilling_v1'
require 'google/apis/cloudresourcemanager_v1' require 'google/apis/cloudresourcemanager_v1'
...@@ -53,30 +54,13 @@ module GoogleApi ...@@ -53,30 +54,13 @@ module GoogleApi
service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header) service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header)
end end
def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:, legacy_abac:) def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:, legacy_abac:, enable_addons: [])
service = Google::Apis::ContainerV1::ContainerService.new service = Google::Apis::ContainerV1beta1::ContainerService.new
service.authorization = access_token service.authorization = access_token
request_body = Google::Apis::ContainerV1::CreateClusterRequest.new( cluster_options = make_cluster_options(cluster_name, cluster_size, machine_type, legacy_abac, enable_addons)
{
"cluster": { request_body = Google::Apis::ContainerV1beta1::CreateClusterRequest.new(cluster_options)
"name": cluster_name,
"initial_node_count": cluster_size,
"node_config": {
"machine_type": machine_type
},
"master_auth": {
"username": CLUSTER_MASTER_AUTH_USERNAME,
"client_certificate_config": {
issue_client_certificate: true
}
},
"legacy_abac": {
"enabled": legacy_abac
}
}
}
)
service.create_cluster(project_id, zone, request_body, options: user_agent_header) service.create_cluster(project_id, zone, request_body, options: user_agent_header)
end end
...@@ -95,6 +79,30 @@ module GoogleApi ...@@ -95,6 +79,30 @@ module GoogleApi
private private
def make_cluster_options(cluster_name, cluster_size, machine_type, legacy_abac, enable_addons)
{
cluster: {
name: cluster_name,
initial_node_count: cluster_size,
node_config: {
machine_type: machine_type
},
master_auth: {
username: CLUSTER_MASTER_AUTH_USERNAME,
client_certificate_config: {
issue_client_certificate: true
}
},
legacy_abac: {
enabled: legacy_abac
},
addons_config: enable_addons.each_with_object({}) do |addon, hash|
hash[addon] = { disabled: false }
end
}
}
end
def token_life_time(expires_at) def token_life_time(expires_at)
DateTime.strptime(expires_at, '%s').to_time.utc - Time.now.utc DateTime.strptime(expires_at, '%s').to_time.utc - Time.now.utc
end end
......
...@@ -3325,6 +3325,9 @@ msgstr "" ...@@ -3325,6 +3325,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster." msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "" msgstr ""
msgid "ClusterIntegration|Cloud Run"
msgstr ""
msgid "ClusterIntegration|Cluster health" msgid "ClusterIntegration|Cluster health"
msgstr "" msgstr ""
...@@ -3364,6 +3367,9 @@ msgstr "" ...@@ -3364,6 +3367,9 @@ msgstr ""
msgid "ClusterIntegration|Did you know?" msgid "ClusterIntegration|Did you know?"
msgstr "" msgstr ""
msgid "ClusterIntegration|Enable Cloud Run on GKE (beta)"
msgstr ""
msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster." msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
msgstr "" msgstr ""
...@@ -3724,6 +3730,9 @@ msgstr "" ...@@ -3724,6 +3730,9 @@ msgstr ""
msgid "ClusterIntegration|Update failed. Please check the logs and try again." msgid "ClusterIntegration|Update failed. Please check the logs and try again."
msgstr "" msgstr ""
msgid "ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster."
msgstr ""
msgid "ClusterIntegration|Validating project billing status" msgid "ClusterIntegration|Validating project billing status"
msgstr "" msgstr ""
...@@ -3760,6 +3769,9 @@ msgstr "" ...@@ -3760,6 +3769,9 @@ msgstr ""
msgid "ClusterIntegration|help page" msgid "ClusterIntegration|help page"
msgstr "" msgstr ""
msgid "ClusterIntegration|installed via %{installed_via}"
msgstr ""
msgid "ClusterIntegration|meets the requirements" msgid "ClusterIntegration|meets the requirements"
msgstr "" msgstr ""
......
...@@ -58,6 +58,10 @@ FactoryBot.define do ...@@ -58,6 +58,10 @@ FactoryBot.define do
platform_kubernetes factory: [:cluster_platform_kubernetes, :configured, :rbac_disabled] platform_kubernetes factory: [:cluster_platform_kubernetes, :configured, :rbac_disabled]
end end
trait :cloud_run_enabled do
provider_gcp factory: [:cluster_provider_gcp, :created, :cloud_run_enabled]
end
trait :disabled do trait :disabled do
enabled false enabled false
end end
......
...@@ -34,5 +34,9 @@ FactoryBot.define do ...@@ -34,5 +34,9 @@ FactoryBot.define do
trait :abac_enabled do trait :abac_enabled do
legacy_abac true legacy_abac true
end end
trait :cloud_run_enabled do
cloud_run true
end
end end
end end
...@@ -54,8 +54,11 @@ describe('Clusters Store', () => { ...@@ -54,8 +54,11 @@ describe('Clusters Store', () => {
environmentsHelpPath: null, environmentsHelpPath: null,
clustersHelpPath: null, clustersHelpPath: null,
deployBoardsHelpPath: null, deployBoardsHelpPath: null,
cloudRunHelpPath: null,
status: mockResponseData.status, status: mockResponseData.status,
statusReason: mockResponseData.status_reason, statusReason: mockResponseData.status_reason,
providerType: null,
preInstalledKnative: false,
rbac: false, rbac: false,
applications: { applications: {
helm: { helm: {
......
# frozen_string_literal: true
require 'spec_helper'
describe './config/initializers/google_api_client.rb' do
subject { Google::Apis::ContainerV1beta1 }
it 'is needed' do |example|
is_expected.not_to be_const_defined(:CloudRunConfig),
<<-MSG.strip_heredoc
The google-api-client gem has been upgraded!
Remove:
#{example.example_group.description}
#{example.file_path}
MSG
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
describe Gitlab::Utils::InlineHash do
describe '.merge_keys' do
subject { described_class.merge_keys(source) }
let(:source) do
{
nested_param: {
key: 'Value'
},
'root_param' => 'Root',
'very' => {
'deep' => {
'nested' => {
'param' => 'Deep nested value'
}
}
}
}
end
it 'transforms a nested hash into a one-level hash' do
is_expected.to eq(
'nested_param.key' => 'Value',
'root_param' => 'Root',
'very.deep.nested.param' => 'Deep nested value'
)
end
it 'retains key insertion order' do
expect(subject.keys)
.to eq(%w(nested_param.key root_param very.deep.nested.param))
end
context 'with a custom connector' do
subject { described_class.merge_keys(source, connector: '::') }
it 'uses the connector to merge keys' do
is_expected.to eq(
'nested_param::key' => 'Value',
'root_param' => 'Root',
'very::deep::nested::param' => 'Deep nested value'
)
end
end
context 'with a starter prefix' do
subject { described_class.merge_keys(source, prefix: 'options') }
it 'prefixes all the keys' do
is_expected.to eq(
'options.nested_param.key' => 'Value',
'options.root_param' => 'Root',
'options.very.deep.nested.param' => 'Deep nested value'
)
end
end
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
describe Gitlab::Utils::SafeInlineHash do
context '.merge_keys!' do
let(:source) { { 'foo' => { 'bar' => 'baz' } } }
let(:validator) { instance_double(Gitlab::Utils::DeepSize, valid?: valid) }
subject { described_class.merge_keys!(source, prefix: 'safe', connector: '::') }
before do
allow(Gitlab::Utils::DeepSize)
.to receive(:new)
.with(source)
.and_return(validator)
end
context 'when hash is too big' do
let(:valid) { false }
it 'raises an exception' do
expect { subject }.to raise_error ArgumentError, 'The Hash is too big'
end
end
context 'when hash has an acceptaable size' do
let(:valid) { true }
it 'returns a result of InlineHash' do
is_expected.to eq('safe::foo::bar' => 'baz')
end
end
end
end
...@@ -68,7 +68,7 @@ describe GoogleApi::CloudPlatform::Client do ...@@ -68,7 +68,7 @@ describe GoogleApi::CloudPlatform::Client do
describe '#projects_zones_clusters_create' do describe '#projects_zones_clusters_create' do
subject do subject do
client.projects_zones_clusters_create( client.projects_zones_clusters_create(
project_id, zone, cluster_name, cluster_size, machine_type: machine_type, legacy_abac: legacy_abac) project_id, zone, cluster_name, cluster_size, machine_type: machine_type, legacy_abac: legacy_abac, enable_addons: enable_addons)
end end
let(:project_id) { 'project-123' } let(:project_id) { 'project-123' }
...@@ -77,39 +77,51 @@ describe GoogleApi::CloudPlatform::Client do ...@@ -77,39 +77,51 @@ describe GoogleApi::CloudPlatform::Client do
let(:cluster_size) { 1 } let(:cluster_size) { 1 }
let(:machine_type) { 'n1-standard-2' } let(:machine_type) { 'n1-standard-2' }
let(:legacy_abac) { true } let(:legacy_abac) { true }
let(:create_cluster_request_body) { double('Google::Apis::ContainerV1::CreateClusterRequest') } let(:enable_addons) { [] }
let(:addons_config) do
enable_addons.each_with_object({}) do |addon, hash|
hash[addon] = { disabled: false }
end
end
let(:cluster_options) do
{
cluster: {
name: cluster_name,
initial_node_count: cluster_size,
node_config: {
machine_type: machine_type
},
master_auth: {
username: 'admin',
client_certificate_config: {
issue_client_certificate: true
}
},
legacy_abac: {
enabled: legacy_abac
},
addons_config: addons_config
}
}
end
let(:create_cluster_request_body) { double('Google::Apis::ContainerV1beta1::CreateClusterRequest') }
let(:operation) { double } let(:operation) { double }
before do before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) allow_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
.to receive(:create_cluster).with(any_args) .to receive(:create_cluster).with(any_args)
.and_return(operation) .and_return(operation)
end end
it 'sets corresponded parameters' do it 'sets corresponded parameters' do
expect_any_instance_of(Google::Apis::ContainerV1::ContainerService) expect_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
.to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options) .to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options)
expect(Google::Apis::ContainerV1::CreateClusterRequest) expect(Google::Apis::ContainerV1beta1::CreateClusterRequest)
.to receive(:new).with( .to receive(:new).with(cluster_options).and_return(create_cluster_request_body)
{
"cluster": {
"name": cluster_name,
"initial_node_count": cluster_size,
"node_config": {
"machine_type": machine_type
},
"master_auth": {
"username": "admin",
"client_certificate_config": {
issue_client_certificate: true
}
},
"legacy_abac": {
"enabled": true
}
}
} ).and_return(create_cluster_request_body)
expect(subject).to eq operation expect(subject).to eq operation
end end
...@@ -118,29 +130,25 @@ describe GoogleApi::CloudPlatform::Client do ...@@ -118,29 +130,25 @@ describe GoogleApi::CloudPlatform::Client do
let(:legacy_abac) { false } let(:legacy_abac) { false }
it 'sets corresponded parameters' do it 'sets corresponded parameters' do
expect_any_instance_of(Google::Apis::ContainerV1::ContainerService) expect_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
.to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options)
expect(Google::Apis::ContainerV1beta1::CreateClusterRequest)
.to receive(:new).with(cluster_options).and_return(create_cluster_request_body)
expect(subject).to eq operation
end
end
context 'create with enable_addons for cloud_run' do
let(:enable_addons) { [:http_load_balancing, :istio_config, :cloud_run_config] }
it 'sets corresponded parameters' do
expect_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
.to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options) .to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options)
expect(Google::Apis::ContainerV1::CreateClusterRequest) expect(Google::Apis::ContainerV1beta1::CreateClusterRequest)
.to receive(:new).with( .to receive(:new).with(cluster_options).and_return(create_cluster_request_body)
{
"cluster": {
"name": cluster_name,
"initial_node_count": cluster_size,
"node_config": {
"machine_type": machine_type
},
"master_auth": {
"username": "admin",
"client_certificate_config": {
issue_client_certificate: true
}
},
"legacy_abac": {
"enabled": false
}
}
} ).and_return(create_cluster_request_body)
expect(subject).to eq operation expect(subject).to eq operation
end end
......
...@@ -16,6 +16,13 @@ describe Clusters::Applications::Knative do ...@@ -16,6 +16,13 @@ describe Clusters::Applications::Knative do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
end end
describe 'when cloud run is enabled' do
let(:cluster) { create(:cluster, :provided_by_gcp, :cloud_run_enabled) }
let(:knative_cloud_run) { create(:clusters_applications_knative, cluster: cluster) }
it { expect(knative_cloud_run).to be_not_installable }
end
describe 'when rbac is not enabled' do describe 'when rbac is not enabled' do
let(:cluster) { create(:cluster, :provided_by_gcp, :rbac_disabled) } let(:cluster) { create(:cluster, :provided_by_gcp, :rbac_disabled) }
let(:knative_no_rbac) { create(:clusters_applications_knative, cluster: cluster) } let(:knative_no_rbac) { create(:clusters_applications_knative, cluster: cluster) }
......
...@@ -756,4 +756,26 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do ...@@ -756,4 +756,26 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end end
end end
end end
describe '#knative_pre_installed?' do
subject { cluster.knative_pre_installed? }
context 'with a GCP provider without cloud_run' do
let(:cluster) { create(:cluster, :provided_by_gcp) }
it { is_expected.to be_falsey }
end
context 'with a GCP provider with cloud_run' do
let(:cluster) { create(:cluster, :provided_by_gcp, :cloud_run_enabled) }
it { is_expected.to be_truthy }
end
context 'with a user provider' do
let(:cluster) { create(:cluster, :provided_by_user) }
it { is_expected.to be_falsey }
end
end
end end
...@@ -166,6 +166,22 @@ describe Clusters::Providers::Gcp do ...@@ -166,6 +166,22 @@ describe Clusters::Providers::Gcp do
end end
end end
describe '#knative_pre_installed?' do
subject { gcp.knative_pre_installed? }
context 'when cluster is cloud_run' do
let(:gcp) { create(:cluster_provider_gcp) }
it { is_expected.to be_falsey }
end
context 'when cluster is not cloud_run' do
let(:gcp) { create(:cluster_provider_gcp, :cloud_run_enabled) }
it { is_expected.to be_truthy }
end
end
describe '#api_client' do describe '#api_client' do
subject { gcp.api_client } subject { gcp.api_client }
......
...@@ -107,6 +107,9 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do ...@@ -107,6 +107,9 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
namespace: 'default' namespace: 'default'
} }
) )
stub_kubeclient_get_cluster_role_binding_error(api_url, 'gitlab-admin')
stub_kubeclient_create_cluster_role_binding(api_url)
end end
end end
...@@ -133,9 +136,6 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do ...@@ -133,9 +136,6 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
context 'With an RBAC cluster' do context 'With an RBAC cluster' do
before do before do
provider.legacy_abac = false provider.legacy_abac = false
stub_kubeclient_get_cluster_role_binding_error(api_url, 'gitlab-admin')
stub_kubeclient_create_cluster_role_binding(api_url)
end end
include_context 'kubernetes information successfully fetched' include_context 'kubernetes information successfully fetched'
...@@ -152,4 +152,22 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do ...@@ -152,4 +152,22 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
it_behaves_like 'kubernetes information not successfully fetched' it_behaves_like 'kubernetes information not successfully fetched'
end end
context 'With a Cloud Run cluster' do
before do
provider.cloud_run = true
end
include_context 'kubernetes information successfully fetched'
it_behaves_like 'success'
it 'has knative pre-installed' do
subject
cluster.reload
expect(cluster.application_knative).to be_present
expect(cluster.application_knative).to be_pre_installed
end
end
end end
...@@ -65,7 +65,7 @@ module GoogleApi ...@@ -65,7 +65,7 @@ module GoogleApi
end end
def cloud_platform_create_cluster_url(project_id, zone) def cloud_platform_create_cluster_url(project_id, zone)
"https://container.googleapis.com/v1/projects/#{project_id}/zones/#{zone}/clusters" "https://container.googleapis.com/v1beta1/projects/#{project_id}/zones/#{zone}/clusters"
end end
def cloud_platform_get_zone_operation_url(project_id, zone, operation_id) def cloud_platform_get_zone_operation_url(project_id, zone, operation_id)
......
...@@ -11,6 +11,20 @@ shared_examples 'cluster application status specs' do |application_name| ...@@ -11,6 +11,20 @@ shared_examples 'cluster application status specs' do |application_name|
end end
end end
describe '#status_states' do
let(:cluster) { create(:cluster, :provided_by_gcp) }
subject { described_class.new(cluster: cluster) }
it 'returns a hash of state values' do
expect(subject.status_states).to include(:installed)
end
it 'returns an integer for installed state value' do
expect(subject.status_states[:installed]).to eq(3)
end
end
describe '.available' do describe '.available' do
subject { described_class.available } subject { described_class.available }
......
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