summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-09-23 00:06:29 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-09-23 00:06:29 +0000
commit98dbb0a488d7b0093f352938210d9578b0f7a8a6 (patch)
tree25654204f8de2672556a696199fa209b8f8ff1b3
parent9ce26d3dfdf4194f32c470cd3102b4376a53ef2f (diff)
downloadgitlab-ce-98dbb0a488d7b0093f352938210d9578b0f7a8a6.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js9
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue9
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue39
-rw-r--r--app/assets/javascripts/clusters/constants.js7
-rw-r--r--app/assets/javascripts/clusters/services/application_state_machine.js25
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js13
-rw-r--r--app/controllers/clusters/clusters_controller.rb1
-rw-r--r--app/models/clusters/applications/helm.rb2
-rw-r--r--app/models/clusters/applications/jupyter.rb2
-rw-r--r--app/models/clusters/applications/knative.rb6
-rw-r--r--app/models/clusters/cluster.rb4
-rw-r--r--app/models/clusters/concerns/application_core.rb2
-rw-r--r--app/models/clusters/concerns/application_status.rb19
-rw-r--r--app/models/clusters/providers/gcp.rb7
-rw-r--r--app/services/boards/issues/list_service.rb6
-rw-r--r--app/services/clusters/gcp/finalize_creation_service.rb8
-rw-r--r--app/services/clusters/gcp/provision_service.rb7
-rw-r--r--app/views/clusters/clusters/gcp/_form.html.haml7
-rw-r--r--app/views/clusters/clusters/show.html.haml3
-rw-r--r--changelogs/unreleased/27502-enable-cloud-run-on-gke.yml5
-rw-r--r--config/initializers/google_api_client.rb17
-rw-r--r--config/sidekiq_queues.yml1
-rw-r--r--db/migrate/20190905140605_add_cloud_run_to_clusters_providers_gcp.rb17
-rw-r--r--db/migrate/20190919162036_add_index_to_clusters_providers_gcp_on_cloud_run.rb17
-rw-r--r--db/schema.rb4
-rw-r--r--doc/user/project/clusters/index.md10
-rw-r--r--lib/gitlab/utils/inline_hash.rb54
-rw-r--r--lib/gitlab/utils/safe_inline_hash.rb30
-rw-r--r--lib/google_api/cloud_platform/client.rb52
-rw-r--r--locale/gitlab.pot12
-rw-r--r--spec/factories/clusters/clusters.rb4
-rw-r--r--spec/factories/clusters/providers/gcp.rb4
-rw-r--r--spec/frontend/clusters/stores/clusters_store_spec.js3
-rw-r--r--spec/initializers/google_api_client_spec.rb17
-rw-r--r--spec/lib/gitlab/utils/inline_hash_spec.rb62
-rw-r--r--spec/lib/gitlab/utils/safe_inline_hash_spec.rb35
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb98
-rw-r--r--spec/models/clusters/applications/knative_spec.rb7
-rw-r--r--spec/models/clusters/cluster_spec.rb22
-rw-r--r--spec/models/clusters/providers/gcp_spec.rb16
-rw-r--r--spec/services/clusters/gcp/finalize_creation_service_spec.rb24
-rw-r--r--spec/support/google_api/cloud_platform_helpers.rb2
-rw-r--r--spec/support/shared_examples/models/cluster_application_status_shared_examples.rb14
43 files changed, 621 insertions, 82 deletions
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index d386960f3b6..7ea8901ecbb 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -41,6 +41,8 @@ export default class Clusters {
managePrometheusPath,
clusterEnvironmentsPath,
hasRbac,
+ providerType,
+ preInstalledKnative,
clusterType,
clusterStatus,
clusterStatusReason,
@@ -50,6 +52,7 @@ export default class Clusters {
environmentsHelpPath,
clustersHelpPath,
deployBoardsHelpPath,
+ cloudRunHelpPath,
clusterId,
} = document.querySelector('.js-edit-cluster-form').dataset;
@@ -65,10 +68,13 @@ export default class Clusters {
environmentsHelpPath,
clustersHelpPath,
deployBoardsHelpPath,
+ cloudRunHelpPath,
);
this.store.setManagePrometheusPath(managePrometheusPath);
this.store.updateStatus(clusterStatus);
this.store.updateStatusReason(clusterStatusReason);
+ this.store.updateProviderType(providerType);
+ this.store.updatePreInstalledKnative(preInstalledKnative);
this.store.updateRbac(hasRbac);
this.service = new ClustersService({
endpoint: statusPath,
@@ -153,6 +159,9 @@ export default class Clusters {
ingressHelpPath: this.state.ingressHelpPath,
managePrometheusPath: this.state.managePrometheusPath,
ingressDnsHelpPath: this.state.ingressDnsHelpPath,
+ cloudRunHelpPath: this.state.cloudRunHelpPath,
+ providerType: this.state.providerType,
+ preInstalledKnative: this.state.preInstalledKnative,
rbac: this.state.rbac,
},
});
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 64364092016..c6c8dc6352c 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -78,6 +78,10 @@ export default {
required: false,
default: false,
},
+ installedVia: {
+ type: String,
+ required: false,
+ },
version: {
type: String,
required: false,
@@ -311,6 +315,11 @@ export default {
>
<span v-else class="js-cluster-application-title">{{ title }}</span>
</strong>
+ <span
+ v-if="installedVia"
+ class="js-cluster-application-installed-via"
+ v-html="installedVia"
+ ></span>
<slot name="description"></slot>
<div v-if="hasError" class="cluster-application-error text-danger prepend-top-10">
<p class="js-cluster-application-general-error-message append-bottom-0">
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 27959898fb7..4d3e759d8d4 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -16,7 +16,7 @@ import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.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 eventHub from '~/clusters/event_hub';
@@ -54,11 +54,26 @@ export default {
required: false,
default: '',
},
+ cloudRunHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
managePrometheusPath: {
type: String,
required: false,
default: '',
},
+ providerType: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ preInstalledKnative: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
rbac: {
type: Boolean,
required: false,
@@ -156,6 +171,25 @@ export default {
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() {
this.helmInstallIllustration = helmInstallIllustration;
@@ -468,6 +502,7 @@ export default {
:installed="applications.knative.installed"
:install-failed="applications.knative.installFailed"
:install-application-request-params="{ hostname: applications.knative.hostname }"
+ :installed-via="installedVia"
:uninstallable="applications.knative.uninstallable"
:uninstall-successful="applications.knative.uninstallSuccessful"
:uninstall-failed="applications.knative.uninstallFailed"
@@ -499,7 +534,7 @@ export default {
</p>
<knative-domain-editor
- v-if="knative.installed || (helmInstalled && rbac)"
+ v-if="(knative.installed || (helmInstalled && rbac)) && !preInstalledKnative"
:knative="knative"
:ingress-dns-help-path="ingressDnsHelpPath"
@save="saveKnativeDomain"
diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js
index 8fd752092c9..c6e4b7951cf 100644
--- a/app/assets/javascripts/clusters/constants.js
+++ b/app/assets/javascripts/clusters/constants.js
@@ -5,6 +5,11 @@ export const CLUSTER_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
export const APPLICATION_STATUS = {
NO_STATUS: null,
@@ -19,6 +24,7 @@ export const APPLICATION_STATUS = {
UNINSTALLING: 'uninstalling',
UNINSTALL_ERRORED: 'uninstall_errored',
ERROR: 'errored',
+ PRE_INSTALLED: 'pre_installed',
};
/*
@@ -29,6 +35,7 @@ export const APPLICATION_INSTALLED_STATUSES = [
APPLICATION_STATUS.INSTALLED,
APPLICATION_STATUS.UPDATING,
APPLICATION_STATUS.UNINSTALLING,
+ APPLICATION_STATUS.PRE_INSTALLED,
];
// These are only used client-side
diff --git a/app/assets/javascripts/clusters/services/application_state_machine.js b/app/assets/javascripts/clusters/services/application_state_machine.js
index 6e632519d8a..6bc4be7b93a 100644
--- a/app/assets/javascripts/clusters/services/application_state_machine.js
+++ b/app/assets/javascripts/clusters/services/application_state_machine.js
@@ -13,6 +13,7 @@ const {
UPDATE_ERRORED,
UNINSTALLING,
UNINSTALL_ERRORED,
+ PRE_INSTALLED,
} = APPLICATION_STATUS;
const applicationStateMachine = {
@@ -63,6 +64,9 @@ const applicationStateMachine = {
uninstallFailed: true,
},
},
+ [PRE_INSTALLED]: {
+ target: PRE_INSTALLED,
+ },
},
},
[NOT_INSTALLABLE]: {
@@ -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]: {
on: {
[UPDATED]: {
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 5cddb4cc098..6464461ea0c 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -35,7 +35,10 @@ export default class ClusterStore {
environmentsHelpPath: null,
clustersHelpPath: null,
deployBoardsHelpPath: null,
+ cloudRunHelpPath: null,
status: null,
+ providerType: null,
+ preInstalledKnative: false,
rbac: false,
statusReason: null,
applications: {
@@ -95,6 +98,7 @@ export default class ClusterStore {
environmentsHelpPath,
clustersHelpPath,
deployBoardsHelpPath,
+ cloudRunHelpPath,
) {
this.state.helpPath = helpPath;
this.state.ingressHelpPath = ingressHelpPath;
@@ -102,6 +106,7 @@ export default class ClusterStore {
this.state.environmentsHelpPath = environmentsHelpPath;
this.state.clustersHelpPath = clustersHelpPath;
this.state.deployBoardsHelpPath = deployBoardsHelpPath;
+ this.state.cloudRunHelpPath = cloudRunHelpPath;
}
setManagePrometheusPath(managePrometheusPath) {
@@ -112,6 +117,14 @@ export default class ClusterStore {
this.state.status = status;
}
+ updateProviderType(providerType) {
+ this.state.providerType = providerType;
+ }
+
+ updatePreInstalledKnative(preInstalledKnative) {
+ this.state.preInstalledKnative = parseBoolean(preInstalledKnative);
+ }
+
updateRbac(rbac) {
this.state.rbac = parseBoolean(rbac);
}
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index 15f1e8284ff..993aba661f3 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -170,6 +170,7 @@ class Clusters::ClustersController < Clusters::BaseController
:zone,
:num_nodes,
:machine_type,
+ :cloud_run,
:legacy_abac
]).merge(
provider_type: :gcp,
diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb
index 455cf200fbc..261f6ce8987 100644
--- a/app/models/clusters/applications/helm.rb
+++ b/app/models/clusters/applications/helm.rb
@@ -27,7 +27,7 @@ module Clusters
def set_initial_status
return unless not_installable?
- self.status = 'installable' if cluster&.platform_kubernetes_active?
+ self.status = status_states[:installable] if cluster&.platform_kubernetes_active?
end
# It can only be uninstalled if there are no other applications installed
diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb
index ec65482a846..ca93bc15be0 100644
--- a/app/models/clusters/applications/jupyter.rb
+++ b/app/models/clusters/applications/jupyter.rb
@@ -23,7 +23,7 @@ module Clusters
return unless cluster&.application_ingress_available?
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
def chart
diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
index a9b9374622d..f2a3695d2eb 100644
--- a/app/models/clusters/applications/knative.rb
+++ b/app/models/clusters/applications/knative.rb
@@ -21,7 +21,7 @@ module Clusters
return unless not_installable?
return unless verify_cluster?
- self.status = 'installable'
+ self.status = status_states[:installable]
end
state_machine :status do
@@ -47,6 +47,10 @@ module Clusters
{ "domain" => hostname }.to_yaml
end
+ def allowed_to_uninstall?
+ !pre_installed?
+ end
+
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 231cadfae05..2df30e8ac36 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -194,6 +194,10 @@ module Clusters
end
end
+ def knative_pre_installed?
+ provider&.knative_pre_installed?
+ end
+
private
def instance_domain
diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb
index d1b57a21a7d..e748c0a855d 100644
--- a/app/models/clusters/concerns/application_core.rb
+++ b/app/models/clusters/concerns/application_core.rb
@@ -15,7 +15,7 @@ module Clusters
def set_initial_status
return unless not_installable?
- self.status = 'installable' if cluster&.application_helm_available?
+ self.status = status_states[:installable] if cluster&.application_helm_available?
end
def can_uninstall?
diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb
index 342d766f723..b63a596dfee 100644
--- a/app/models/clusters/concerns/application_status.rb
+++ b/app/models/clusters/concerns/application_status.rb
@@ -28,6 +28,13 @@ module Clusters
state :uninstalling, value: 7
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
transition [:installable, :errored, :installed, :updated, :update_errored, :uninstall_errored] => :scheduled
end
@@ -41,6 +48,10 @@ module Clusters
transition [:updating] => :updated
end
+ event :make_pre_installed do
+ transition any => :pre_installed
+ end
+
event :make_errored do
transition any - [:updating, :uninstalling] => :errored
transition [:updating] => :update_errored
@@ -90,12 +101,18 @@ module Clusters
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?
installed? || updated? || update_errored?
end
def available?
- installed? || updated?
+ pre_installed? || installed? || updated?
end
def update_in_progress?
diff --git a/app/models/clusters/providers/gcp.rb b/app/models/clusters/providers/gcp.rb
index 390748bf252..043765f79ac 100644
--- a/app/models/clusters/providers/gcp.rb
+++ b/app/models/clusters/providers/gcp.rb
@@ -10,6 +10,9 @@ module Clusters
default_value_for :zone, 'us-central1-a'
default_value_for :num_nodes, 3
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,
mode: :per_attribute_iv,
@@ -77,6 +80,10 @@ module Clusters
@api_client ||= GoogleApi::CloudPlatform::Client.new(access_token, nil)
end
+
+ def knative_pre_installed?
+ cloud_run?
+ end
end
end
end
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 10eb1141f59..37a74cd1b00 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -11,9 +11,11 @@ module Boards
# rubocop: disable CodeReuse/ActiveRecord
def metadata
+ issues = Issue.arel_table
keys = metadata_fields.keys
- columns = metadata_fields.values_at(*keys).join(', ')
- results = Issue.where(id: fetch_issues.select('issues.id')).pluck(columns)
+ # TODO: eliminate need for SQL literal fragment
+ 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)]
end
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb
index c5cde831964..0aff1bcc8b9 100644
--- a/app/services/clusters/gcp/finalize_creation_service.rb
+++ b/app/services/clusters/gcp/finalize_creation_service.rb
@@ -11,6 +11,7 @@ module Clusters
configure_provider
create_gitlab_service_account!
configure_kubernetes
+ configure_pre_installed_knative if provider.knative_pre_installed?
cluster.save!
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
log_service_error(e.class.name, provider.id, e.message)
@@ -48,6 +49,13 @@ module Clusters
token: request_kubernetes_token)
end
+ def configure_pre_installed_knative
+ knative = cluster.build_application_knative(
+ hostname: 'example.com'
+ )
+ knative.make_pre_installed!
+ end
+
def request_kubernetes_token
Clusters::Kubernetes::FetchKubernetesTokenService.new(
kube_client,
diff --git a/app/services/clusters/gcp/provision_service.rb b/app/services/clusters/gcp/provision_service.rb
index 80040511ec2..7dc2d3c32f1 100644
--- a/app/services/clusters/gcp/provision_service.rb
+++ b/app/services/clusters/gcp/provision_service.rb
@@ -3,6 +3,8 @@
module Clusters
module Gcp
class ProvisionService
+ CLOUD_RUN_ADDONS = %i[http_load_balancing istio_config cloud_run_config].freeze
+
attr_reader :provider
def execute(provider)
@@ -22,13 +24,16 @@ module Clusters
private
def get_operation_id
+ enable_addons = provider.cloud_run? ? CLOUD_RUN_ADDONS : []
+
operation = provider.api_client.projects_zones_clusters_create(
provider.gcp_project_id,
provider.zone,
provider.cluster.name,
provider.num_nodes,
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'
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
index 4d3e3359ea0..196ad422766 100644
--- a/app/views/clusters/clusters/gcp/_form.html.haml
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -65,6 +65,13 @@
%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 }
+ .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
= field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'),
label_class: 'label-bold' }
diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml
index cccba48624b..8b9844bcfc1 100644
--- a/app/views/clusters/clusters/show.html.haml
+++ b/app/views/clusters/clusters/show.html.haml
@@ -23,12 +23,15 @@
cluster_type: @cluster.cluster_type,
cluster_status: @cluster.status_name,
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'),
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'),
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'),
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,
cluster_id: @cluster.id } }
diff --git a/changelogs/unreleased/27502-enable-cloud-run-on-gke.yml b/changelogs/unreleased/27502-enable-cloud-run-on-gke.yml
new file mode 100644
index 00000000000..77b365f17db
--- /dev/null
+++ b/changelogs/unreleased/27502-enable-cloud-run-on-gke.yml
@@ -0,0 +1,5 @@
+---
+title: Enable Cloud Run on GKE cluster creation
+merge_request: 16566
+author:
+type: added
diff --git a/config/initializers/google_api_client.rb b/config/initializers/google_api_client.rb
new file mode 100644
index 00000000000..611726a20c7
--- /dev/null
+++ b/config/initializers/google_api_client.rb
@@ -0,0 +1,17 @@
+# 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
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index f37cd518d48..6b0741667bd 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -117,3 +117,4 @@
- [jira_connect, 1]
- [update_external_pull_requests, 3]
- [refresh_license_compliance_checks, 2]
+ - [design_management_new_version, 1]
diff --git a/db/migrate/20190905140605_add_cloud_run_to_clusters_providers_gcp.rb b/db/migrate/20190905140605_add_cloud_run_to_clusters_providers_gcp.rb
new file mode 100644
index 00000000000..e7ffd7cd4d3
--- /dev/null
+++ b/db/migrate/20190905140605_add_cloud_run_to_clusters_providers_gcp.rb
@@ -0,0 +1,17 @@
+# 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
diff --git a/db/migrate/20190919162036_add_index_to_clusters_providers_gcp_on_cloud_run.rb b/db/migrate/20190919162036_add_index_to_clusters_providers_gcp_on_cloud_run.rb
new file mode 100644
index 00000000000..8e0bde97cc1
--- /dev/null
+++ b/db/migrate/20190919162036_add_index_to_clusters_providers_gcp_on_cloud_run.rb
@@ -0,0 +1,17 @@
+# 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
diff --git a/db/schema.rb b/db/schema.rb
index b60593132f5..392db66f5b6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# 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
enable_extension "pg_trgm"
@@ -965,6 +965,8 @@ ActiveRecord::Schema.define(version: 2019_09_18_104222) do
t.text "encrypted_access_token"
t.string "encrypted_access_token_iv"
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
end
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 97fa973d3e3..49878978154 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -154,6 +154,7 @@ new Kubernetes cluster to your project:
- **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)
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.
1. Finally, click the **Create Kubernetes cluster** button.
@@ -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
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
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/22011) in GitLab 11.5.
diff --git a/lib/gitlab/utils/inline_hash.rb b/lib/gitlab/utils/inline_hash.rb
new file mode 100644
index 00000000000..5985e4da845
--- /dev/null
+++ b/lib/gitlab/utils/inline_hash.rb
@@ -0,0 +1,54 @@
+# 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
diff --git a/lib/gitlab/utils/safe_inline_hash.rb b/lib/gitlab/utils/safe_inline_hash.rb
new file mode 100644
index 00000000000..644d87c6876
--- /dev/null
+++ b/lib/gitlab/utils/safe_inline_hash.rb
@@ -0,0 +1,30 @@
+# 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
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index 9f01a3f97ce..eaf94708282 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -2,6 +2,7 @@
require 'google/apis/compute_v1'
require 'google/apis/container_v1'
+require 'google/apis/container_v1beta1'
require 'google/apis/cloudbilling_v1'
require 'google/apis/cloudresourcemanager_v1'
@@ -53,30 +54,13 @@ module GoogleApi
service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header)
end
- def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:, legacy_abac:)
- service = Google::Apis::ContainerV1::ContainerService.new
+ def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:, legacy_abac:, enable_addons: [])
+ service = Google::Apis::ContainerV1beta1::ContainerService.new
service.authorization = access_token
- request_body = Google::Apis::ContainerV1::CreateClusterRequest.new(
- {
- "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
- }
- }
- }
- )
+ cluster_options = make_cluster_options(cluster_name, cluster_size, machine_type, legacy_abac, enable_addons)
+
+ request_body = Google::Apis::ContainerV1beta1::CreateClusterRequest.new(cluster_options)
service.create_cluster(project_id, zone, request_body, options: user_agent_header)
end
@@ -95,6 +79,30 @@ module GoogleApi
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)
DateTime.strptime(expires_at, '%s').to_time.utc - Time.now.utc
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 089a6dd5b1d..c9e40aca796 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3325,6 +3325,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cloud Run"
+msgstr ""
+
msgid "ClusterIntegration|Cluster health"
msgstr ""
@@ -3364,6 +3367,9 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable Cloud Run on GKE (beta)"
+msgstr ""
+
msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
msgstr ""
@@ -3724,6 +3730,9 @@ msgstr ""
msgid "ClusterIntegration|Update failed. Please check the logs and try again."
msgstr ""
+msgid "ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster."
+msgstr ""
+
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
@@ -3760,6 +3769,9 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
+msgid "ClusterIntegration|installed via %{installed_via}"
+msgstr ""
+
msgid "ClusterIntegration|meets the requirements"
msgstr ""
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index d294e6d055e..29aea5e403e 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -58,6 +58,10 @@ FactoryBot.define do
platform_kubernetes factory: [:cluster_platform_kubernetes, :configured, :rbac_disabled]
end
+ trait :cloud_run_enabled do
+ provider_gcp factory: [:cluster_provider_gcp, :created, :cloud_run_enabled]
+ end
+
trait :disabled do
enabled false
end
diff --git a/spec/factories/clusters/providers/gcp.rb b/spec/factories/clusters/providers/gcp.rb
index 22462651b6a..7fdcdebad34 100644
--- a/spec/factories/clusters/providers/gcp.rb
+++ b/spec/factories/clusters/providers/gcp.rb
@@ -34,5 +34,9 @@ FactoryBot.define do
trait :abac_enabled do
legacy_abac true
end
+
+ trait :cloud_run_enabled do
+ cloud_run true
+ end
end
end
diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js
index ee3b7d8aa90..5ee06eb44c9 100644
--- a/spec/frontend/clusters/stores/clusters_store_spec.js
+++ b/spec/frontend/clusters/stores/clusters_store_spec.js
@@ -54,8 +54,11 @@ describe('Clusters Store', () => {
environmentsHelpPath: null,
clustersHelpPath: null,
deployBoardsHelpPath: null,
+ cloudRunHelpPath: null,
status: mockResponseData.status,
statusReason: mockResponseData.status_reason,
+ providerType: null,
+ preInstalledKnative: false,
rbac: false,
applications: {
helm: {
diff --git a/spec/initializers/google_api_client_spec.rb b/spec/initializers/google_api_client_spec.rb
new file mode 100644
index 00000000000..44a1bc0836c
--- /dev/null
+++ b/spec/initializers/google_api_client_spec.rb
@@ -0,0 +1,17 @@
+# 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
diff --git a/spec/lib/gitlab/utils/inline_hash_spec.rb b/spec/lib/gitlab/utils/inline_hash_spec.rb
new file mode 100644
index 00000000000..806117eddd0
--- /dev/null
+++ b/spec/lib/gitlab/utils/inline_hash_spec.rb
@@ -0,0 +1,62 @@
+# 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
diff --git a/spec/lib/gitlab/utils/safe_inline_hash_spec.rb b/spec/lib/gitlab/utils/safe_inline_hash_spec.rb
new file mode 100644
index 00000000000..8d8163f3baa
--- /dev/null
+++ b/spec/lib/gitlab/utils/safe_inline_hash_spec.rb
@@ -0,0 +1,35 @@
+# 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
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index c24998d32f8..2253feb376d 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -68,7 +68,7 @@ describe GoogleApi::CloudPlatform::Client do
describe '#projects_zones_clusters_create' do
subject do
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
let(:project_id) { 'project-123' }
@@ -77,39 +77,51 @@ describe GoogleApi::CloudPlatform::Client do
let(:cluster_size) { 1 }
let(:machine_type) { 'n1-standard-2' }
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 }
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)
.and_return(operation)
end
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::ContainerV1::CreateClusterRequest)
- .to receive(:new).with(
- {
- "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(Google::Apis::ContainerV1beta1::CreateClusterRequest)
+ .to receive(:new).with(cluster_options).and_return(create_cluster_request_body)
expect(subject).to eq operation
end
@@ -118,29 +130,25 @@ describe GoogleApi::CloudPlatform::Client do
let(:legacy_abac) { false }
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)
- expect(Google::Apis::ContainerV1::CreateClusterRequest)
- .to receive(:new).with(
- {
- "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(Google::Apis::ContainerV1beta1::CreateClusterRequest)
+ .to receive(:new).with(cluster_options).and_return(create_cluster_request_body)
expect(subject).to eq operation
end
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 3825994b733..16247026e34 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -16,6 +16,13 @@ describe Clusters::Applications::Knative do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
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
let(:cluster) { create(:cluster, :provided_by_gcp, :rbac_disabled) }
let(:knative_no_rbac) { create(:clusters_applications_knative, cluster: cluster) }
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index b8a6fd6f914..df6263eeed2 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -756,4 +756,26 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
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
diff --git a/spec/models/clusters/providers/gcp_spec.rb b/spec/models/clusters/providers/gcp_spec.rb
index 785db4febe0..7ac1bbfafd8 100644
--- a/spec/models/clusters/providers/gcp_spec.rb
+++ b/spec/models/clusters/providers/gcp_spec.rb
@@ -166,6 +166,22 @@ describe Clusters::Providers::Gcp do
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
subject { gcp.api_client }
diff --git a/spec/services/clusters/gcp/finalize_creation_service_spec.rb b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
index 5f91acb8e84..43dbea959a2 100644
--- a/spec/services/clusters/gcp/finalize_creation_service_spec.rb
+++ b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
@@ -107,6 +107,9 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
namespace: 'default'
}
)
+
+ stub_kubeclient_get_cluster_role_binding_error(api_url, 'gitlab-admin')
+ stub_kubeclient_create_cluster_role_binding(api_url)
end
end
@@ -133,9 +136,6 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
context 'With an RBAC cluster' do
before do
provider.legacy_abac = false
-
- stub_kubeclient_get_cluster_role_binding_error(api_url, 'gitlab-admin')
- stub_kubeclient_create_cluster_role_binding(api_url)
end
include_context 'kubernetes information successfully fetched'
@@ -152,4 +152,22 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
it_behaves_like 'kubernetes information not successfully fetched'
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
diff --git a/spec/support/google_api/cloud_platform_helpers.rb b/spec/support/google_api/cloud_platform_helpers.rb
index a1328ef0d13..38ffca8c5ae 100644
--- a/spec/support/google_api/cloud_platform_helpers.rb
+++ b/spec/support/google_api/cloud_platform_helpers.rb
@@ -65,7 +65,7 @@ module GoogleApi
end
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
def cloud_platform_get_zone_operation_url(project_id, zone, operation_id)
diff --git a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
index 5341aacb445..6f06d323a82 100644
--- a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
@@ -11,6 +11,20 @@ shared_examples 'cluster application status specs' do |application_name|
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
subject { described_class.available }