Commit c1f270b8 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 7f4a1ba8
......@@ -8,7 +8,7 @@ import Flash from '../flash';
import Poll from '../lib/utils/poll';
import initSettingsPanels from '../settings_panels';
import eventHub from './event_hub';
import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX } from './constants';
import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX, CROSSPLANE } from './constants';
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';
import Applications from './components/applications.vue';
......@@ -39,6 +39,7 @@ export default class Clusters {
installKnativePath,
updateKnativePath,
installElasticStackPath,
installCrossplanePath,
installPrometheusPath,
managePrometheusPath,
clusterEnvironmentsPath,
......@@ -83,6 +84,7 @@ export default class Clusters {
installHelmEndpoint: installHelmPath,
installIngressEndpoint: installIngressPath,
installCertManagerEndpoint: installCertManagerPath,
installCrossplaneEndpoint: installCrossplanePath,
installRunnerEndpoint: installRunnerPath,
installPrometheusEndpoint: installPrometheusPath,
installJupyterEndpoint: installJupyterPath,
......@@ -227,6 +229,7 @@ export default class Clusters {
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
......@@ -238,6 +241,7 @@ export default class Clusters {
eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain');
eventHub.$off('setKnativeHostname');
eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
}
......@@ -404,18 +408,33 @@ export default class Clusters {
}
installApplication({ id: appId, params }) {
this.store.updateAppProperty(appId, 'requestReason', null);
this.store.updateAppProperty(appId, 'statusReason', null);
return Clusters.validateInstallation(appId, params)
.then(() => {
this.store.updateAppProperty(appId, 'requestReason', null);
this.store.updateAppProperty(appId, 'statusReason', null);
this.store.installApplication(appId);
// eslint-disable-next-line promise/no-nesting
this.service.installApplication(appId, params).catch(() => {
this.store.notifyInstallFailure(appId);
this.store.updateAppProperty(
appId,
'requestReason',
s__('ClusterIntegration|Request to begin installing failed'),
);
});
})
.catch(error => this.store.updateAppProperty(appId, 'validationError', error));
}
this.store.installApplication(appId);
static validateInstallation(appId, params) {
return new Promise((resolve, reject) => {
if (appId === CROSSPLANE && !params.stack) {
reject(s__('ClusterIntegration|Select a stack to install Crossplane.'));
return;
}
return this.service.installApplication(appId, params).catch(() => {
this.store.notifyInstallFailure(appId);
this.store.updateAppProperty(
appId,
'requestReason',
s__('ClusterIntegration|Request to begin installing failed'),
);
resolve();
});
}
......@@ -463,6 +482,12 @@ export default class Clusters {
this.store.updateAppProperty(appId, 'hostname', data.hostname);
}
setCrossplaneProviderStack(data) {
const appId = data.id;
this.store.updateAppProperty(appId, 'stack', data.stack.code);
this.store.updateAppProperty(appId, 'validationError', null);
}
destroy() {
this.destroyed = true;
......
<script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { s__ } from '../../locale';
export default {
name: 'CrossplaneProviderStack',
components: {
GlDropdown,
GlDropdownItem,
Icon,
},
props: {
stacks: {
type: Array,
required: false,
default: () => [
{
name: s__('Google Cloud Platform'),
code: 'gcp',
},
{
name: s__('Amazon Web Services'),
code: 'aws',
},
{
name: s__('Microsoft Azure'),
code: 'azure',
},
{
name: s__('Rook'),
code: 'rook',
},
],
},
crossplane: {
type: Object,
required: true,
},
},
computed: {
dropdownText() {
const result = this.stacks.reduce((map, obj) => {
// eslint-disable-next-line no-param-reassign
map[obj.code] = obj.name;
return map;
}, {});
const { stack } = this.crossplane;
if (stack !== '') {
return result[stack];
}
return s__('Select Stack');
},
validationError() {
return this.crossplane.validationError;
},
},
methods: {
selectStack(stack) {
this.$emit('set', stack);
},
},
};
</script>
<template>
<div>
<label>
{{ s__('ClusterIntegration|Enabled stack') }}
</label>
<gl-dropdown
:disabled="crossplane.installed"
:text="dropdownText"
toggle-class="dropdown-menu-toggle gl-field-error-outline"
class="w-100"
:class="{ 'gl-show-field-errors': validationError }"
>
<gl-dropdown-item v-for="stack in stacks" :key="stack.code" @click="selectStack(stack)">
<span class="ml-1">{{ stack.name }}</span>
</gl-dropdown-item>
</gl-dropdown>
<span v-if="validationError" class="gl-field-error">{{ validationError }}</span>
<p class="form-text text-muted">
{{ s__(`You must select a stack for configuring your cloud provider. Learn more about`) }}
<a
href="https://crossplane.io/docs/master/stacks-guide.html"
target="_blank"
rel="noopener noreferrer"
>{{ __('Crossplane') }}</a
>
</p>
</div>
</template>
......@@ -50,6 +50,7 @@ export const JUPYTER = 'jupyter';
export const KNATIVE = 'knative';
export const RUNNER = 'runner';
export const CERT_MANAGER = 'cert_manager';
export const CROSSPLANE = 'crossplane';
export const PROMETHEUS = 'prometheus';
export const ELASTIC_STACK = 'elastic_stack';
......
......@@ -7,6 +7,7 @@ export default class ClusterService {
helm: this.options.installHelmEndpoint,
ingress: this.options.installIngressEndpoint,
cert_manager: this.options.installCertManagerEndpoint,
crossplane: this.options.installCrossplaneEndpoint,
runner: this.options.installRunnerEndpoint,
prometheus: this.options.installPrometheusEndpoint,
jupyter: this.options.installJupyterEndpoint,
......
......@@ -6,6 +6,7 @@ import {
KNATIVE,
CERT_MANAGER,
ELASTIC_STACK,
CROSSPLANE,
RUNNER,
APPLICATION_INSTALLED_STATUSES,
APPLICATION_STATUS,
......@@ -26,6 +27,7 @@ const applicationInitialState = {
uninstallable: false,
uninstallFailed: false,
uninstallSuccessful: false,
validationError: null,
};
export default class ClusterStore {
......@@ -58,6 +60,11 @@ export default class ClusterStore {
title: s__('ClusterIntegration|Cert-Manager'),
email: null,
},
crossplane: {
...applicationInitialState,
title: s__('ClusterIntegration|Crossplane'),
stack: null,
},
runner: {
...applicationInitialState,
title: s__('ClusterIntegration|GitLab Runner'),
......@@ -203,6 +210,9 @@ export default class ClusterStore {
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
this.state.applications.cert_manager.email || serverAppEntry.email;
} else if (appId === CROSSPLANE) {
this.state.applications.crossplane.stack =
this.state.applications.crossplane.stack || serverAppEntry.stack;
} else if (appId === JUPYTER) {
this.state.applications.jupyter.hostname = this.updateHostnameIfUnset(
this.state.applications.jupyter.hostname,
......
......@@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController
end
def cluster_application_params
params.permit(:application, :hostname, :kibana_hostname, :email)
params.permit(:application, :hostname, :kibana_hostname, :email, :stack)
end
def cluster_application_destroy_params
......
......@@ -17,6 +17,7 @@ class Clusters::ClustersController < Clusters::BaseController
end
before_action only: [:show] do
push_frontend_feature_flag(:enable_cluster_application_elastic_stack)
push_frontend_feature_flag(:enable_cluster_application_crossplane)
end
helper_method :token_in_session
......
# frozen_string_literal: true
module Clusters
module Applications
class Crossplane < ApplicationRecord
VERSION = '0.4.1'
self.table_name = 'clusters_applications_crossplane'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
default_value_for :version, VERSION
default_value_for :stack do |crossplane|
''
end
validates :stack, presence: true
def chart
'crossplane/crossplane'
end
def repository
'https://charts.crossplane.io/alpha'
end
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: 'crossplane',
repository: repository,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files
)
end
def values
crossplane_values.to_yaml
end
private
def crossplane_values
{
"clusterStacks" => {
self.stack => {
"deploy" => true,
"version" => "alpha"
}
}
}
end
end
end
end
......@@ -14,6 +14,7 @@ module Clusters
Applications::Helm.application_name => Applications::Helm,
Applications::Ingress.application_name => Applications::Ingress,
Applications::CertManager.application_name => Applications::CertManager,
Applications::Crossplane.application_name => Applications::Crossplane,
Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner,
Applications::Jupyter.application_name => Applications::Jupyter,
......@@ -47,6 +48,7 @@ module Clusters
has_one_cluster_application :helm
has_one_cluster_application :ingress
has_one_cluster_application :cert_manager
has_one_cluster_application :crossplane
has_one_cluster_application :prometheus
has_one_cluster_application :runner
has_one_cluster_application :jupyter
......
......@@ -10,6 +10,7 @@ class ClusterApplicationEntity < Grape::Entity
expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) }
expose :kibana_hostname, if: -> (e, _) { e.respond_to?(:kibana_hostname) }
expose :email, if: -> (e, _) { e.respond_to?(:email) }
expose :stack, if: -> (e, _) { e.respond_to?(:stack) }
expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }
expose :can_uninstall?, as: :can_uninstall
end
......@@ -27,6 +27,10 @@ module Clusters
application.email = params[:email]
end
if application.has_attribute?(:stack)
application.stack = params[:stack]
end
if application.respond_to?(:oauth_application)
application.oauth_application = create_oauth_application(application, request)
end
......@@ -64,7 +68,7 @@ module Clusters
end
def invalid_application?
unknown_application? || (application_name == Applications::ElasticStack.application_name && !Feature.enabled?(:enable_cluster_application_elastic_stack))
unknown_application? || (application_name == Applications::ElasticStack.application_name && !Feature.enabled?(:enable_cluster_application_elastic_stack)) || (application_name == Applications::Crossplane.application_name && !Feature.enabled?(:enable_cluster_application_crossplane))
end
def unknown_application?
......
......@@ -12,6 +12,7 @@
install_helm_path: clusterable.install_applications_cluster_path(@cluster, :helm),
install_ingress_path: clusterable.install_applications_cluster_path(@cluster, :ingress),
install_cert_manager_path: clusterable.install_applications_cluster_path(@cluster, :cert_manager),
install_crossplane_path: clusterable.install_applications_cluster_path(@cluster, :crossplane),
install_prometheus_path: clusterable.install_applications_cluster_path(@cluster, :prometheus),
install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner),
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
......
---
title: Support for Crossplane as a managed app
merge_request: 18797
author: Mahendra Bagul
type: added
# frozen_string_literal: true
class CreateClustersApplicationsCrossplane < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :clusters_applications_crossplane do |t|
t.timestamps_with_timezone null: false
t.references :cluster, null: false, index: false, foreign_key: { on_delete: :cascade }
t.integer :status, null: false
t.string :version, null: false, limit: 255
t.string :stack, null: false, limit: 255
t.text :status_reason
t.index :cluster_id, unique: true
end
end
end
......@@ -1078,6 +1078,17 @@ ActiveRecord::Schema.define(version: 2019_11_14_173624) do
t.index ["cluster_id"], name: "index_clusters_applications_cert_managers_on_cluster_id", unique: true
end
create_table "clusters_applications_crossplane", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.bigint "cluster_id", null: false
t.integer "status", null: false
t.string "version", limit: 255, null: false
t.string "stack", limit: 255, null: false
t.text "status_reason"
t.index ["cluster_id"], name: "index_clusters_applications_crossplane_on_cluster_id", unique: true
end
create_table "clusters_applications_elastic_stacks", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
......@@ -4222,6 +4233,7 @@ ActiveRecord::Schema.define(version: 2019_11_14_173624) do
add_foreign_key "clusters", "projects", column: "management_project_id", name: "fk_f05c5e5a42", on_delete: :nullify
add_foreign_key "clusters", "users", on_delete: :nullify
add_foreign_key "clusters_applications_cert_managers", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_crossplane", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_elastic_stacks", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_helm", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_ingress", "clusters", on_delete: :cascade
......
......@@ -305,7 +305,7 @@ The following documentation relates to the DevOps **Configure** stage:
| Configure Topics | Description |
|:-----------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
| [Auto DevOps](topics/autodevops/index.md) | Automatically employ a complete DevOps lifecycle. |
| [Create Kubernetes clusters on GKE](user/project/clusters/add_remove_clusters.md#add-new-gke-cluster) | Use Google Kubernetes Engine and GitLab. |
| [Create Kubernetes clusters](user/project/clusters/add_remove_clusters.md#add-new-cluster) | Use Kubernetes and GitLab. |
| [Executable Runbooks](user/project/clusters/runbooks/index.md) | Documented procedures that explain how to carry out particular processes. |
| [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. |
| [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. |
......
......@@ -100,7 +100,7 @@ To make full use of Auto DevOps, you will need:
To enable deployments, you will need:
1. A [Kubernetes 1.12+ cluster](../../user/project/clusters/index.md) for the project. The easiest
way is to add a [new GKE cluster using the GitLab UI](../../user/project/clusters/add_remove_clusters.md#add-new-gke-cluster).
way is to add a [new cluster using the GitLab UI](../../user/project/clusters/add_remove_clusters.md#add-new-cluster).
1. NGINX Ingress. You can deploy it to your Kubernetes cluster by installing
the [GitLab-managed app for Ingress](../../user/clusters/applications.md#ingress),
once you have configured GitLab's Kubernetes integration in the previous step.
......
......@@ -35,7 +35,7 @@ for an overview of how this is accomplished in GitLab!**
To create an executable runbook, you will need:
1. **Kubernetes** - A Kubernetes cluster is required to deploy the rest of the applications.
The simplest way to get started is to add a cluster using [GitLab's GKE integration](../add_remove_clusters.md#add-new-gke-cluster).
The simplest way to get started is to add a cluster using one of [GitLab's integrations](../add_remove_clusters.md#add-new-cluster).
1. **Helm Tiller** - Helm is a package manager for Kubernetes and is required to install
all the other applications. It is installed in its own pod inside the cluster which
can run the Helm CLI in a safe environment.
......@@ -60,7 +60,7 @@ the components outlined above and the preloaded demo runbook.
### 1. Add a Kubernetes cluster
Follow the steps outlined in [Add new GKE cluster](../add_remove_clusters.md#add-new-gke-cluster)
Follow the steps outlined in [Add new cluster](../add_remove_clusters.md#add-new-cluster)
to add a Kubernetes cluster to your project.
### 2. Install Helm Tiller, Ingress, and JupyterHub
......
......@@ -41,7 +41,7 @@ To run Knative on GitLab, you will need:
- If you are planning on deploying a serverless application, clone the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
1. **Kubernetes Cluster:** An RBAC-enabled Kubernetes cluster is required to deploy Knative.
The simplest way to get started is to add a cluster using [GitLab's GKE integration](../add_remove_clusters.md#add-new-gke-cluster).
The simplest way to get started is to add a cluster using [GitLab's GKE integration](../add_remove_clusters.md#gke-cluster).
The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory.
1. **Helm Tiller:** Helm is a package manager for Kubernetes and is required to install
Knative.
......@@ -64,6 +64,8 @@ To run Knative on GitLab, you will need:
using our [runtimes](https://gitlab.com/gitlab-org/serverless/runtimes).
1. **Prometheus** (optional): Installing Prometheus allows you to monitor the scale and traffic of your serverless function/application.
See [Installing Applications](../index.md#installing-applications) for more information.
1. **Logging** (optional): Configuring logging allows you to view and search request logs for your serverless function/application.
See [Configuring logging](#configuring-logging) for more information.
## Installing Knative via GitLab's Kubernetes integration
......@@ -166,13 +168,61 @@ You must do the following:
or [serverless applications](#deploying-serverless-applications) onto your
cluster.
## Deploying functions
## Configuring logging
> Introduced in GitLab 11.6.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/33330) in GitLab 12.5.
### Prerequisites
- A GitLab-managed cluster.
- `kubectl` installed and working.
Running `kubectl` commands on your cluster requires setting up access to the
cluster first. For clusters created on:
- GKE, see [GKE Cluster Access](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl)
- Other platforms, see [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/).
### Enable request log template
Run the following command to enable request logs:
```shell
kubectl edit cm -n knative-serving config-observability
```
Copy the `logging.request-log-template` from the `data._example` field to the data field one level up in the hierarchy.
### Enable request logs
Run the following commands to install Elasticsearch, Kibana, and Filebeat into a `kube-logging` namespace and configure all nodes to forward logs using Filebeat:
```shell
kubectl apply -f https://gitlab.com/gitlab-org/serverless/configurations/knative/raw/v0.7.0/kube-logging-filebeat.yaml
kubectl label nodes --all beta.kubernetes.io/filebeat-ready="true"
```
### Viewing request logs
To view request logs:
1. Run `kubectl proxy`.
1. Navigate to Kibana UI.
Or:
Using functions is useful for dealing with independent events without needing
to maintain a complex unified infrastructure. This allows you to focus on a
single task that can be executed/scaled automatically and independently.
1. Open the Kibana UI.
1. Click on **Discover**, then select `filebeat-*` from the dropdown on the left.
1. Enter `kubernetes.container.name:"queue-proxy" AND message:/httpRequest/` into the search box.
## Supported runtimes
Serverless functions for GitLab can be written in 6 supported languages:
- NodeJS and Ruby, with GitLab-managed and OpenFaas runtimes.
- C#, Go, PHP, and Python with OpenFaaS runtimes only.
### GitLab managed runtimes
Currently the following [runtimes](https://gitlab.com/gitlab-org/serverless/runtimes) are offered:
......@@ -182,6 +232,31 @@ Currently the following [runtimes](https://gitlab.com/gitlab-org/serverless/runt
`Dockerfile` presence is assumed when a runtime is not specified.
### OpenFaaS runtimes
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/29253) in GitLab 12.5.
[OpenFaaS classic runtimes](https://github.com/openfaas/templates#templates-in-store) can be used with GitLab serverless.
Runtimes are specified using the pattern: `openfaas/classic/<template_name>`. The following
example shows how to define a function in `serverless.yml` using an OpenFaaS runtime:
```yaml
hello:
source: ./hello
runtime: openfaas/classic/ruby
description: "Ruby function using OpenFaaS classic runtime"
```
`handler` is not needed for OpenFaaS functions. The location of the handler is defined
by the conventions of the runtime.
See the [`ruby-openfaas-function`](https://gitlab.com/knative-examples/ruby-openfaas-function)
project for an example of a function using an OpenFaaS runtime.
## Deploying functions
> Introduced in GitLab 11.6.
You can find and import all the files referenced in this doc in the
**[functions example project](https://gitlab.com/knative-examples/functions)**.
......@@ -322,7 +397,7 @@ Running functions locally requires:
- Go 1.12 or newer installed.
- Docker Engine installed and running.
- `gitlabktl` installed using the Go package manager:
```shell
GO111MODULE=on go get gitlab.com/gitlab-org/gitlabktl
```
......@@ -352,6 +427,10 @@ To run a function locally:
> Introduced in GitLab 11.5.
Serverless applications are the building block of serverless functions. They are useful in scenarios where an existing
runtime does not meet the needs of an application, such as one written in a language that has no runtime available. Note
though that serverless applications should be stateless!
NOTE: **Note:**
You can reference and import the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
......
......@@ -73,6 +73,7 @@ module Gitlab
clusters_applications_helm: count(::Clusters::Applications::Helm.available),
clusters_applications_ingress: count(::Clusters::Applications::Ingress.available),
clusters_applications_cert_managers: count(::Clusters::Applications::CertManager.available),
clusters_applications_crossplane: count(::Clusters::Applications::Crossplane.available),
clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.available),
clusters_applications_runner: count(::Clusters::Applications::Runner.available),
clusters_applications_knative: count(::Clusters::Applications::Knative.available),
......
......@@ -1495,6 +1495,9 @@ msgstr ""
msgid "Amazon EKS integration allows you to provision EKS clusters from GitLab."
msgstr ""
msgid "Amazon Web Services"
msgstr ""
msgid "Amazon authentication is not %{link_start}correctly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
......@@ -3646,6 +3649,12 @@ msgstr ""
msgid "ClusterIntegration|Creating Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Crossplane"
msgstr ""
msgid "ClusterIntegration|Crossplane enables declarative provisioning of managed services from your cloud of choice using %{kubectl} or %{gitlabIntegrationLink}. Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on."
msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
......@@ -3661,6 +3670,9 @@ msgstr ""
msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
msgstr ""
msgid "ClusterIntegration|Enabled stack"
msgstr ""
msgid "ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster"
msgstr ""
......@@ -3709,6 +3721,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab-managed cluster"
msgstr ""
msgid "ClusterIntegration|Gitlab Integration"
msgstr ""
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
......@@ -4030,6 +4045,9 @@ msgstr ""
msgid "ClusterIntegration|Select a region to choose a VPC"
msgstr ""
msgid "ClusterIntegration|Select a stack to install Crossplane."
msgstr ""
msgid "ClusterIntegration|Select machine type"
msgstr ""
......@@ -4982,6 +5000,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
msgid "Crossplane"
msgstr ""
msgid "Current Branch"
msgstr ""
......@@ -8442,6 +8463,9 @@ msgstr ""
msgid "Golden Tanuki"
msgstr ""
msgid "Google Cloud Platform"
msgstr ""
msgid "Google Code import"
msgstr ""
......@@ -10885,6 +10909,9 @@ msgstr ""
msgid "Metrics|e.g. req/sec"
msgstr ""
msgid "Microsoft Azure"
msgstr ""
msgid "Migrated %{success_count}/%{total_count} files."
msgstr ""
......@@ -14669,6 +14696,9 @@ msgstr ""
msgid "Rollback"
msgstr ""
msgid "Rook"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
......@@ -15243,6 +15273,9 @@ msgstr ""
msgid "Select Page"
msgstr ""
msgid "Select Stack"
msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
......@@ -19854,6 +19887,9 @@ msgstr ""
msgid "You must provide your current password in order to change it."
msgstr ""
msgid "You must select a stack for configuring your cloud provider. Learn more about"
msgstr ""
msgid "You need a different license to enable FileLocks feature"
msgstr ""
......
......@@ -83,6 +83,11 @@ FactoryBot.define do
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
factory :clusters_applications_crossplane, class: Clusters::Applications::Crossplane do
stack { 'gcp' }
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus do
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
......
......@@ -37,6 +37,7 @@
"hostname": { "type": ["string", "null"] },
"kibana_hostname": { "type": ["string", "null"] },
"email": { "type": ["string", "null"] },
"stack": { "type": ["string", "null"] },
"update_available": { "type": ["boolean", "null"] },
"can_uninstall": { "type": "boolean" }
},
......
......@@ -286,16 +286,21 @@ describe('Clusters', () => {
});
describe('installApplication', () => {
it.each(APPLICATIONS)('tries to install %s', applicationId => {
jest.spyOn(cluster.service, 'installApplication').mockResolvedValueOnce();
it.each(APPLICATIONS)('tries to install %s', (applicationId, done) => {
jest.spyOn(cluster.service, 'installApplication').mockResolvedValue();
cluster.store.state.applications[applicationId].status = INSTALLABLE;
cluster.installApplication({ id: applicationId });
expect(cluster.store.state.applications[applicationId].status).toEqual(INSTALLING);
expect(cluster.store.state.applications[applicationId].requestReason).toEqual(null);
expect(cluster.service.installApplication).toHaveBeenCalledWith(applicationId, undefined);
// eslint-disable-next-line promise/valid-params
cluster
.installApplication({ id: applicationId })
.then(() => {
expect(cluster.store.state.applications[applicationId].status).toEqual(INSTALLING);
expect(cluster.store.state.applications[applicationId].requestReason).toEqual(null);
expect(cluster.service.installApplication).toHaveBeenCalledWith(applicationId, undefined);
done();
})
.catch();
});
it('sets error request status when the request fails', () => {
......
......@@ -6,6 +6,7 @@ import { APPLICATIONS_MOCK_STATE } from '../services/mock_data';
import eventHub from '~/clusters/event_hub';
import { shallowMount } from '@vue/test-utils';
import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
import CrossplaneProviderStack from '~/clusters/components/crossplane_provider_stack.vue';
describe('Applications', () => {
let vm;
......@@ -16,6 +17,7 @@ describe('Applications', () => {
gon.features = gon.features || {};
gon.features.enableClusterApplicationElasticStack = true;
gon.features.enableClusterApplicationCrossplane = true;
});
afterEach(() => {
......@@ -42,6 +44,10 @@ describe('Applications', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).not.toBeNull();
});
it('renders a row for Crossplane', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-crossplane')).not.toBeNull();
});
it('renders a row for Prometheus', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).not.toBeNull();
});
......@@ -83,6 +89,10 @@ describe('Applications', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).not.toBeNull();
});
it('renders a row for Crossplane', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-crossplane')).not.toBeNull();
});
it('renders a row for Prometheus', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).not.toBeNull();
});
......@@ -124,6 +134,10 @@ describe('Applications', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).not.toBeNull();
});
it('renders a row for Crossplane', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-crossplane')).not.toBeNull();
});
it('renders a row for Prometheus', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).not.toBeNull();
});
......@@ -179,6 +193,7 @@ describe('Applications', () => {
},
helm: { title: 'Helm Tiller' },
cert_manager: { title: 'Cert-Manager' },
crossplane: { title: 'Crossplane', stack: '' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
......@@ -390,6 +405,32 @@ describe('Applications', () => {
});
});
describe('Crossplane application', () => {
const propsData = {
applications: {
...APPLICATIONS_MOCK_STATE,
crossplane: {
title: 'Crossplane',
stack: {
code: '',
},
},
},
};
let wrapper;
beforeEach(() => {
wrapper = shallowMount(Applications, { propsData });
});
afterEach(() => {
wrapper.destroy();
});
it('renders the correct Component', () => {
const crossplane = wrapper.find(CrossplaneProviderStack);
expect(crossplane.exists()).toBe(true);
});
});
describe('Elastic Stack application', () => {
describe('with ingress installed with ip & elastic stack installable', () => {
it('renders hostname active input', () => {
......
import { shallowMount } from '@vue/test-utils';
import { GlDropdownItem } from '@gitlab/ui';
import CrossplaneProviderStack from '~/clusters/components/crossplane_provider_stack.vue';
describe('CrossplaneProviderStack component', () => {
let wrapper;
const defaultProps = {
stacks: [
{
name: 'Google Cloud Platform',
code: 'gcp',
},
{
name: 'Amazon Web Services',
code: 'aws',
},
],
};
function createComponent(props = {}) {
const propsData = {
...defaultProps,
...props,
};
wrapper = shallowMount(CrossplaneProviderStack, {
propsData,
});
}
beforeEach(() => {
const crossplane = {
title: 'crossplane',
stack: '',
};
createComponent({ crossplane });
});
const findDropdownElements = () => wrapper.findAll(GlDropdownItem);
const findFirstDropdownElement = () => findDropdownElements().at(0);
afterEach(() => {
wrapper.destroy();
});
it('renders all of the available stacks in the dropdown', () => {
const dropdownElements = findDropdownElements();
expect(dropdownElements.length).toBe(defaultProps.stacks.length);
defaultProps.stacks.forEach((stack, index) =>
expect(dropdownElements.at(index).text()).toEqual(stack.name),
);
});
it('displays the correct label for the first dropdown item if a stack is selected', () => {
const crossplane = {
title: 'crossplane',
stack: 'gcp',
};
createComponent({ crossplane });
expect(wrapper.vm.dropdownText).toBe('Google Cloud Platform');
});
it('emits the "set" event with the selected stack value', () => {
const crossplane = {
title: 'crossplane',
stack: 'gcp',
};
createComponent({ crossplane });
findFirstDropdownElement().vm.$emit('click');
expect(wrapper.emitted().set[0][0].code).toEqual('gcp');
});
it('it renders the correct dropdown text when no stack is selected', () => {
expect(wrapper.vm.dropdownText).toBe('Select Stack');
});
});
......@@ -52,9 +52,15 @@ const CLUSTERS_MOCK_DATA = {
email: 'test@example.com',
can_uninstall: false,
},
{
name: 'crossplane',
status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
can_uninstall: false,
},
{
name: 'elastic_stack',
status: APPLICATION_STATUS.INSTALLING,
status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
can_uninstall: false,
},
......@@ -104,6 +110,12 @@ const CLUSTERS_MOCK_DATA = {
status_reason: 'Cannot connect',
email: 'test@example.com',
},
{
name: 'crossplane',
status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
stack: 'gcp',
},
{
name: 'elastic_stack',
status: APPLICATION_STATUS.ERROR,
......@@ -116,6 +128,7 @@ const CLUSTERS_MOCK_DATA = {
POST: {
'/gitlab-org/gitlab-shell/clusters/1/applications/helm': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/ingress': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/crossplane': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/cert_manager': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/runner': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {},
......@@ -138,6 +151,7 @@ const DEFAULT_APPLICATION_STATE = {
const APPLICATIONS_MOCK_STATE = {
helm: { title: 'Helm Tiller', status: 'installable' },
ingress: { title: 'Ingress', status: 'installable' },
crossplane: { title: 'Crossplane', status: 'installable', stack: '' },
cert_manager: { title: 'Cert-Manager', status: 'installable' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
......
......@@ -71,6 +71,7 @@ describe('Clusters Store', () => {
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
ingress: {
title: 'Ingress',
......@@ -84,6 +85,7 @@ describe('Clusters Store', () => {
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
runner: {
title: 'GitLab Runner',
......@@ -100,6 +102,7 @@ describe('Clusters Store', () => {
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
prometheus: {
title: 'Prometheus',
......@@ -111,6 +114,7 @@ describe('Clusters Store', () => {
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
jupyter: {
title: 'JupyterHub',
......@@ -123,6 +127,7 @@ describe('Clusters Store', () => {
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
knative: {
title: 'Knative',
......@@ -140,6 +145,7 @@ describe('Clusters Store', () => {
uninstallFailed: false,
updateSuccessful: false,
updateFailed: false,
validationError: null,
},
cert_manager: {
title: 'Cert-Manager',
......@@ -152,11 +158,12 @@ describe('Clusters Store', () => {
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
elastic_stack: {
title: 'Elastic Stack',
status: mockResponseData.applications[7].status,
installFailed: false,
status: APPLICATION_STATUS.INSTALLABLE,
installFailed: true,
statusReason: mockResponseData.applications[7].status_reason,
requestReason: null,
kibana_hostname: '',
......@@ -164,6 +171,19 @@ describe('Clusters Store', () => {
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
crossplane: {
title: 'Crossplane',
status: APPLICATION_STATUS.INSTALLABLE,
installFailed: true,
statusReason: mockResponseData.applications[8].status_reason,
requestReason: null,
installed: false,
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
},
environments: [],
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Gfm::ReferenceRewriter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Gfm::UploadsRewriter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::BulkImporting do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Caching, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Client do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::DiffNoteImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::DiffNotesImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::IssueAndLabelLinksImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::IssuesImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LabelLinksImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LfsObjectImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::NoteImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::NotesImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::PullRequestsImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::ReleasesImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Importer::RepositoryImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::LabelFinder, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::MarkdownText do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::PageCounter, :clean_gitlab_redis_cache do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::ParallelImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::ParallelScheduling do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation::DiffNote do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation::ExposeAttribute do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation::Issue do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation::Note do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation::PullRequest do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation::ToHash do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation::User do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::Representation do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::SequentialImporter do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
......
......@@ -44,6 +44,7 @@ describe Gitlab::UsageData do
create(:clusters_applications_ingress, :installed, cluster: gcp_cluster)
create(:clusters_applications_cert_manager, :installed, cluster: gcp_cluster)
create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster)
create(:clusters_applications_crossplane, :installed, cluster: gcp_cluster)
create(:clusters_applications_runner, :installed, cluster: gcp_cluster)
create(:clusters_applications_knative, :installed, cluster: gcp_cluster)
create(:clusters_applications_elastic_stack, :installed, cluster: gcp_cluster)
......@@ -140,6 +141,7 @@ describe Gitlab::UsageData do
clusters_applications_ingress
clusters_applications_cert_managers
clusters_applications_prometheus
clusters_applications_crossplane
clusters_applications_runner
clusters_applications_knative
clusters_applications_elastic_stack
......@@ -222,6 +224,7 @@ describe Gitlab::UsageData do
expect(count_data[:clusters_applications_helm]).to eq(1)
expect(count_data[:clusters_applications_ingress]).to eq(1)
expect(count_data[:clusters_applications_cert_managers]).to eq(1)
expect(count_data[:clusters_applications_crossplane]).to eq(1)
expect(count_data[:clusters_applications_prometheus]).to eq(1)
expect(count_data[:clusters_applications_runner]).to eq(1)
expect(count_data[:clusters_applications_knative]).to eq(1)
......
# frozen_string_literal: true
require 'spec_helper'
describe Clusters::Applications::Crossplane do
let(:crossplane) { create(:clusters_applications_crossplane) }
include_examples 'cluster application core specs', :clusters_applications_crossplane
include_examples 'cluster application status specs', :clusters_applications_crossplane
include_examples 'cluster application version specs', :clusters_applications_crossplane
include_examples 'cluster application initial status specs'
describe 'validations' do
it { is_expected.to validate_presence_of(:stack) }
end
describe '#can_uninstall?' do
subject { crossplane.can_uninstall? }
it { is_expected.to be_truthy }
end
describe '#install_command' do
let(:stack) { 'gcp' }
subject { crossplane.install_command }
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) }
it 'is initialized with crossplane arguments' do
expect(subject.name).to eq('crossplane')
expect(subject.chart).to eq('crossplane/crossplane')
expect(subject.repository).to eq('https://charts.crossplane.io/alpha')
expect(subject.version).to eq('0.4.1')
expect(subject).to be_rbac
end
context 'application failed to install previously' do
let(:crossplane) { create(:clusters_applications_crossplane, :errored, version: '0.0.1') }
it 'is initialized with the locked version' do
expect(subject.version).to eq('0.4.1')
end
end
end
describe '#files' do
let(:application) { crossplane }
let(:values) { subject[:'values.yaml'] }
subject { application.files }
it 'includes crossplane specific keys in the values.yaml file' do
expect(values).to include('clusterStacks')
end
end
end
......@@ -515,6 +515,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
let!(:helm) { create(:clusters_applications_helm, cluster: cluster) }
let!(:ingress) { create(:clusters_applications_ingress, cluster: cluster) }
let!(:cert_manager) { create(:clusters_applications_cert_manager, cluster: cluster) }
let!(:crossplane) { create(:clusters_applications_crossplane, cluster: cluster) }
let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
let!(:runner) { create(:clusters_applications_runner, cluster: cluster) }
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) }
......@@ -522,7 +523,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
let!(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: cluster) }
it 'returns a list of created applications' do
is_expected.to contain_exactly(helm, ingress, cert_manager, prometheus, runner, jupyter, knative, elastic_stack)
is_expected.to contain_exactly(helm, ingress, cert_manager, crossplane, prometheus, runner, jupyter, knative, elastic_stack)
end
end
end
......
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