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 {
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,
},
});
......
......@@ -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">
......
......@@ -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"
......
......@@ -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
......
......@@ -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]: {
......
......@@ -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);
}
......
......@@ -170,6 +170,7 @@ class Clusters::ClustersController < Clusters::BaseController
:zone,
:num_nodes,
:machine_type,
:cloud_run,
:legacy_abac
]).merge(
provider_type: :gcp,
......
......@@ -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
......
......@@ -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
......
......@@ -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,
......
......@@ -194,6 +194,10 @@ module Clusters
end
end
def knative_pre_installed?
provider&.knative_pre_installed?
end
private
def instance_domain
......
......@@ -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?
......
......@@ -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?
......
......@@ -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
......@@ -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
......
......@@ -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,
......
......@@ -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'
......
......@@ -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' }
......
......@@ -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 } }
......
---
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 @@
- [jira_connect, 1]
- [update_external_pull_requests, 3]
- [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 @@
#
# 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
......
......@@ -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.
......
# 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 @@
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
......
......@@ -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 ""
......
......@@ -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
......
......@@ -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
......@@ -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: {
......
# 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
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
......
......@@ -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) }
......
......@@ -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
......@@ -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 }
......
......@@ -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
......@@ -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)
......
......@@ -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 }
......
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