Commit 5dc0577b authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'triggermesh-phase1-knative' into 'master'

Allow to install Knative as cluster application

See merge request gitlab-org/gitlab-ce!22593
parents fadaa3d1 c2097ed7
......@@ -28,6 +28,7 @@ export default class Clusters {
installIngressPath,
installRunnerPath,
installJupyterPath,
installKnativePath,
installPrometheusPath,
managePrometheusPath,
clusterStatus,
......@@ -49,6 +50,7 @@ export default class Clusters {
installRunnerEndpoint: installRunnerPath,
installPrometheusEndpoint: installPrometheusPath,
installJupyterEndpoint: installJupyterPath,
installKnativeEndpoint: installKnativePath,
});
this.installApplication = this.installApplication.bind(this);
......
......@@ -7,6 +7,7 @@ import helmLogo from 'images/cluster_app_logos/helm.png';
import jeagerLogo from 'images/cluster_app_logos/jeager.png';
import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
import knativeLogo from 'images/cluster_app_logos/knative.png';
import meltanoLogo from 'images/cluster_app_logos/meltano.png';
import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
import { s__, sprintf } from '../../locale';
......@@ -53,6 +54,7 @@ export default {
jeagerLogo,
jupyterhubLogo,
kubernetesLogo,
knativeLogo,
meltanoLogo,
prometheusLogo,
}),
......@@ -136,6 +138,9 @@ export default {
jupyterHostname() {
return this.applications.jupyter.hostname;
},
knativeInstalled() {
return this.applications.knative.status === APPLICATION_STATUS.INSTALLED;
},
},
created() {
this.helmInstallIllustration = helmInstallIllustration;
......@@ -321,7 +326,6 @@ export default {
:request-reason="applications.jupyter.requestReason"
:install-application-request-params="{ hostname: applications.jupyter.hostname }"
:disabled="!helmInstalled"
class="hide-bottom-border rounded-bottom"
title-link="https://jupyterhub.readthedocs.io/en/stable/"
>
<div slot="description">
......@@ -371,6 +375,58 @@ export default {
</template>
</div>
</application-row>
<application-row
id="knative"
:logo-url="knativeLogo"
:title="applications.knative.title"
:status="applications.knative.status"
:status-reason="applications.knative.statusReason"
:request-status="applications.knative.requestStatus"
:request-reason="applications.knative.requestReason"
:install-application-request-params="{ hostname: applications.knative.hostname}"
:disabled="!helmInstalled"
class="hide-bottom-border rounded-bottom"
title-link="https://github.com/knative/docs"
>
<div slot="description">
<p>
{{ s__(`ClusterIntegration|A Knative build extends Kubernetes
and utilizes existing Kubernetes primitives to provide you with
the ability to run on-cluster container builds from source.
For example, you can write a build that uses Kubernetes-native
resources to obtain your source code from a repository,
build it into container a image, and then run that image.`) }}
</p>
<template v-if="knativeInstalled">
<div class="form-group">
<label for="knative-domainname">
{{ s__('ClusterIntegration|Knative Domain Name:') }}
</label>
<input
id="knative-domainname"
v-model="applications.knative.hostname"
type="text"
class="form-control js-domainname"
readonly
/>
</div>
</template>
<template v-else>
<div class="form-group">
<label for="knative-domainname">
{{ s__('ClusterIntegration|Knative Domain Name:') }}
</label>
<input
id="knative-domainname"
v-model="applications.knative.hostname"
type="text"
class="form-control js-domainname"
/>
</div>
</template>
</div>
</application-row>
</div>
</section>
</template>
......@@ -16,3 +16,4 @@ export const REQUEST_SUCCESS = 'request-success';
export const REQUEST_FAILURE = 'request-failure';
export const INGRESS = 'ingress';
export const JUPYTER = 'jupyter';
export const KNATIVE = 'knative';
......@@ -9,6 +9,7 @@ export default class ClusterService {
runner: this.options.installRunnerEndpoint,
prometheus: this.options.installPrometheusEndpoint,
jupyter: this.options.installJupyterEndpoint,
knative: this.options.installKnativeEndpoint,
};
}
......
import { s__ } from '../../locale';
import { INGRESS, JUPYTER } from '../constants';
import { INGRESS, JUPYTER, KNATIVE } from '../constants';
export default class ClusterStore {
constructor() {
......@@ -46,6 +46,14 @@ export default class ClusterStore {
requestReason: null,
hostname: null,
},
knative: {
title: s__('ClusterIntegration|Knative'),
status: null,
statusReason: null,
requestStatus: null,
requestReason: null,
hostname: null,
},
},
};
}
......@@ -93,6 +101,9 @@ export default class ClusterStore {
(this.state.applications.ingress.externalIp
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
: '');
} else if (appId === KNATIVE) {
this.state.applications.knative.hostname =
serverAppEntry.hostname || this.state.applications.knative.hostname;
}
});
}
......
# frozen_string_literal: true
module Clusters
module Applications
class Knative < ActiveRecord::Base
VERSION = '0.1.3'.freeze
REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze
# This is required for helm version <= 2.10.x in order to support
# Setting up CRDs
ISTIO_CRDS = 'https://storage.googleapis.com/triggermesh-charts/istio-crds.yaml'.freeze
self.table_name = 'clusters_applications_knative'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
default_value_for :version, VERSION
validates :hostname, presence: true, hostname: true
def chart
'knative/knative'
end
def values
{ "domain" => hostname }.to_yaml
end
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files,
repository: REPOSITORY,
preinstall: install_script
)
end
private
def install_script
["/usr/bin/kubectl apply -f #{ISTIO_CRDS} >/dev/null"]
end
end
end
end
......@@ -12,7 +12,8 @@ module Clusters
Applications::Ingress.application_name => Applications::Ingress,
Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner,
Applications::Jupyter.application_name => Applications::Jupyter
Applications::Jupyter.application_name => Applications::Jupyter,
Applications::Knative.application_name => Applications::Knative
}.freeze
DEFAULT_ENVIRONMENT = '*'.freeze
......@@ -35,6 +36,7 @@ module Clusters
has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus'
has_one :application_runner, class_name: 'Clusters::Applications::Runner'
has_one :application_jupyter, class_name: 'Clusters::Applications::Jupyter'
has_one :application_knative, class_name: 'Clusters::Applications::Knative'
has_many :kubernetes_namespaces
has_one :kubernetes_namespace, -> { order(id: :desc) }, class_name: 'Clusters::KubernetesNamespace'
......@@ -100,7 +102,8 @@ module Clusters
application_ingress || build_application_ingress,
application_prometheus || build_application_prometheus,
application_runner || build_application_runner,
application_jupyter || build_application_jupyter
application_jupyter || build_application_jupyter,
application_knative || build_application_knative
]
end
......
......@@ -26,6 +26,7 @@ module Clusters
algorithm: 'aes-256-cbc'
before_validation :enforce_namespace_to_lower_case
before_validation :enforce_ca_whitespace_trimming
validates :namespace,
allow_blank: true,
......@@ -201,6 +202,11 @@ module Clusters
self.namespace = self.namespace&.downcase
end
def enforce_ca_whitespace_trimming
self.ca_pem = self.ca_pem&.strip
self.token = self.token&.strip
end
def prevent_modification
return unless managed?
......
......@@ -45,7 +45,8 @@ module Clusters
"ingress" => -> (cluster) { cluster.application_ingress || cluster.build_application_ingress },
"prometheus" => -> (cluster) { cluster.application_prometheus || cluster.build_application_prometheus },
"runner" => -> (cluster) { cluster.application_runner || cluster.build_application_runner },
"jupyter" => -> (cluster) { cluster.application_jupyter || cluster.build_application_jupyter }
"jupyter" => -> (cluster) { cluster.application_jupyter || cluster.build_application_jupyter },
"knative" => -> (cluster) { cluster.application_knative || cluster.build_application_knative }
}
end
......
......@@ -13,6 +13,7 @@
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),
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
toggle_status: @cluster.enabled? ? 'true': 'false',
cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason,
......
---
title: Introduce Knative support
author: Chris Baumbauer
merge_request: 43959
type: added
# frozen_string_literal: true
class AddKnativeApplication < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table "clusters_applications_knative" do |t|
t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade }
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "status", null: false
t.string "version", null: false
t.string "hostname"
t.text "status_reason"
end
end
end
......@@ -705,6 +705,16 @@ ActiveRecord::Schema.define(version: 20181107054254) do
t.text "status_reason"
end
create_table "clusters_applications_knative", force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "status", null: false
t.string "version", null: false
t.string "hostname"
t.text "status_reason"
end
create_table "clusters_applications_prometheus", force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "status", null: false
......@@ -2422,6 +2432,7 @@ ActiveRecord::Schema.define(version: 20181107054254) do
add_foreign_key "clusters_applications_ingress", "clusters", name: "fk_753a7b41c1", on_delete: :cascade
add_foreign_key "clusters_applications_jupyter", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_jupyter", "oauth_applications", on_delete: :nullify
add_foreign_key "clusters_applications_knative", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_prometheus", "clusters", name: "fk_557e773639", on_delete: :cascade
add_foreign_key "clusters_applications_runners", "ci_runners", column: "runner_id", name: "fk_02de2ded36", on_delete: :nullify
add_foreign_key "clusters_applications_runners", "clusters", on_delete: :cascade
......
......@@ -211,7 +211,7 @@ added directly to your configured cluster. Those applications are needed for
[Review Apps](../../../ci/review_apps/index.md) and [deployments](../../../ci/environments.md).
NOTE: **Note:**
The applications will be installed in a dedicated namespace called
With the exception of Knative, the applications will be installed in a dedicated namespace called
`gitlab-managed-apps`. In case you have added an existing Kubernetes cluster
with Tiller already installed, you should be careful as GitLab cannot
detect it. By installing it via the applications will result into having it
......@@ -224,6 +224,7 @@ twice, which can lead to confusion during deployments.
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found at [Nurtch Documentation](http://docs.nurtch.com/en/latest). **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
| [Knative](https://cloud.google.com/knative) | 0.1.2 | Knative provides a platform to create, deploy, and manage serverless workloads from a Kubernetes cluster. It is used in conjunction with, and includes [Istio](https://istio.io) to provide an external IP address for all programs hosted by Knative. You will be prompted to enter a wildcard domain where your applications will be exposed. Configure your DNS server to use the external IP address for that domain. For any application created and installed, they will be accessible as <program_name>.<kubernetes_namespace>.<domain_name>. **Note**: This will require your kubernetes cluster to have RBAC enabled. | [knative/knative](https://storage.googleapis.com/triggermesh-charts)
## Getting the external IP address
......@@ -232,6 +233,10 @@ You need a load balancer installed in your cluster in order to obtain the
external IP address with the following procedure. It can be deployed using the
[**Ingress** application](#installing-applications).
NOTE: **Note:**
Knative will include its own load balancer in the form of [Istio](https://istio.io).
At this time, to determine the external IP address, you will need to follow the manual approach.
In order to publish your web application, you first need to find the external IP
address associated to your load balancer.
......@@ -262,6 +267,12 @@ run the following command:
kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
```
NOTE: **Note:**
For Istio/Knative, the command will be different:
```bash
kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
```
Otherwise, you can list the IP addresses of all load balancers:
```bash
......
......@@ -4,22 +4,27 @@ module Gitlab
class InstallCommand
include BaseCommand
attr_reader :name, :files, :chart, :version, :repository
attr_reader :name, :files, :chart, :version, :repository, :preinstall, :postinstall
def initialize(name:, chart:, files:, rbac:, version: nil, repository: nil)
def initialize(name:, chart:, files:, rbac:, version: nil, repository: nil, preinstall: nil, postinstall: nil)
@name = name
@chart = chart
@version = version
@rbac = rbac
@files = files
@repository = repository
@preinstall = preinstall
@postinstall = postinstall
end
def generate_script
super + [
init_command,
repository_command,
script_command
repository_update_command,
preinstall_command,
install_command,
postinstall_command
].compact.join("\n")
end
......@@ -37,12 +42,24 @@ module Gitlab
['helm', 'repo', 'add', name, repository].shelljoin if repository
end
def script_command
def repository_update_command
'helm repo update >/dev/null' if repository
end
def install_command
command = ['helm', 'install', chart] + install_command_flags
command.shelljoin + " >/dev/null\n"
end
def preinstall_command
preinstall.join("\n") if preinstall
end
def postinstall_command
postinstall.join("\n") if postinstall
end
def install_command_flags
name_flag = ['--name', name]
namespace_flag = ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE]
......
......@@ -62,6 +62,7 @@ module Gitlab
clusters_applications_ingress: count(::Clusters::Applications::Ingress.installed),
clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.installed),
clusters_applications_runner: count(::Clusters::Applications::Runner.installed),
clusters_applications_knative: count(::Clusters::Applications::Knative.installed),
in_review_folder: count(::Environment.in_review_folder),
groups: count(Group),
issues: count(Issue),
......
......@@ -1352,6 +1352,9 @@ msgstr ""
msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
msgstr ""
msgid "ClusterIntegration|A Knative build extends Kubernetes and utilizes existing Kubernetes primitives to provide you with the ability to run on-cluster container builds from source. For example, you can write a build that uses Kubernetes-native resources to obtain your source code from a repository, build it into container a image, and then run that image."
msgstr ""
msgid "ClusterIntegration|API URL"
msgstr ""
......@@ -1502,6 +1505,12 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
msgstr ""
msgid "ClusterIntegration|Knative"
msgstr ""
msgid "ClusterIntegration|Knative Domain Name:"
msgstr ""
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
......
......@@ -57,6 +57,11 @@ FactoryBot.define do
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
factory :clusters_applications_knative, class: Clusters::Applications::Knative do
hostname 'example.com'
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
factory :clusters_applications_jupyter, class: Clusters::Applications::Jupyter do
oauth_application factory: :oauth_application
cluster factory: %i(cluster with_installed_helm provided_by_gcp project)
......
......@@ -23,6 +23,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub' },
knative: { title: 'Knative' },
},
});
});
......@@ -46,6 +47,10 @@ describe('Applications', () => {
it('renders a row for Jupyter', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-jupyter')).not.toBe(null);
});
it('renders a row for Knative', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBe(null);
});
});
describe('Ingress application', () => {
......@@ -63,6 +68,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
knative: { title: 'Knative', hostname: '' },
},
});
......@@ -86,6 +92,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
knative: { title: 'Knative', hostname: '' },
},
});
......@@ -105,6 +112,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
knative: { title: 'Knative', hostname: '' },
},
});
......@@ -123,6 +131,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
knative: { title: 'Knative', hostname: '', status: 'installable' },
},
});
......@@ -139,6 +148,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
knative: { title: 'Knative', hostname: '', status: 'installable' },
},
});
......@@ -155,6 +165,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' },
knative: { title: 'Knative', status: 'installed', hostname: '' },
},
});
......@@ -171,6 +182,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'not_installable' },
knative: { title: 'Knative' },
},
});
});
......
......@@ -33,6 +33,11 @@ const CLUSTERS_MOCK_DATA = {
status: APPLICATION_STATUS.INSTALLING,
status_reason: 'Cannot connect',
},
{
name: 'knative',
status: APPLICATION_STATUS.INSTALLING,
status_reason: 'Cannot connect',
},
],
},
},
......@@ -67,6 +72,11 @@ const CLUSTERS_MOCK_DATA = {
status: APPLICATION_STATUS.INSTALLABLE,
status_reason: 'Cannot connect',
},
{
name: 'knative',
status: APPLICATION_STATUS.INSTALLABLE,
status_reason: 'Cannot connect',
},
],
},
},
......@@ -77,6 +87,7 @@ const CLUSTERS_MOCK_DATA = {
'/gitlab-org/gitlab-shell/clusters/1/applications/runner': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/knative': {},
},
};
......
......@@ -100,6 +100,14 @@ describe('Clusters Store', () => {
requestReason: null,
hostname: '',
},
knative: {
title: 'Knative',
status: mockResponseData.applications[5].status,
statusReason: mockResponseData.applications[5].status_reason,
requestStatus: null,
requestReason: null,
hostname: null,
},
},
});
});
......
......@@ -5,6 +5,8 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
let(:repository) { 'https://repository.example.com' }
let(:rbac) { false }
let(:version) { '1.2.3' }
let(:preinstall) { nil }
let(:postinstall) { nil }
let(:install_command) do
described_class.new(
......@@ -13,7 +15,9 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
rbac: rbac,
files: files,
version: version,
repository: repository
repository: repository,
preinstall: preinstall,
postinstall: postinstall
)
end
......@@ -24,6 +28,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
<<~EOS
helm init --client-only >/dev/null
helm repo add app-name https://repository.example.com
helm repo update >/dev/null
#{helm_install_comand}
EOS
end
......@@ -51,6 +56,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
<<~EOS
helm init --client-only >/dev/null
helm repo add app-name https://repository.example.com
helm repo update >/dev/null
#{helm_install_command}
EOS
end
......@@ -99,6 +105,53 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
end
end
context 'when there is a pre-install script' do
let(:preinstall) { ['/bin/date', '/bin/true'] }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
helm repo add app-name https://repository.example.com
helm repo update >/dev/null
#{helm_install_command}
EOS
end
let(:helm_install_command) do
<<~EOS.strip
/bin/date
/bin/true
helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
EOS
end
end
end
context 'when there is a post-install script' do
let(:postinstall) { ['/bin/date', "/bin/false\n"] }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
helm repo add app-name https://repository.example.com
helm repo update >/dev/null
#{helm_install_command}
EOS
end
let(:helm_install_command) do
<<~EOS.strip
helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
/bin/date
/bin/false
EOS
end
end
end
context 'when there is no ca.pem file' do
let(:files) { { 'file.txt': 'some content' } }
......@@ -107,6 +160,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
<<~EOS
helm init --client-only >/dev/null
helm repo add app-name https://repository.example.com
helm repo update >/dev/null
#{helm_install_command}
EOS
end
......@@ -131,6 +185,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
<<~EOS
helm init --client-only >/dev/null
helm repo add app-name https://repository.example.com
helm repo update >/dev/null
#{helm_install_command}
EOS
end
......
......@@ -20,6 +20,7 @@ describe Gitlab::UsageData do
create(:clusters_applications_ingress, :installed, cluster: gcp_cluster)
create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster)
create(:clusters_applications_runner, :installed, cluster: gcp_cluster)
create(:clusters_applications_knative, :installed, cluster: gcp_cluster)
end
subject { described_class.data }
......@@ -81,6 +82,7 @@ describe Gitlab::UsageData do
clusters_applications_ingress
clusters_applications_prometheus
clusters_applications_runner
clusters_applications_knative
in_review_folder
groups
issues
......@@ -126,6 +128,7 @@ describe Gitlab::UsageData do
expect(count_data[:clusters_applications_ingress]).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)
end
it 'works when queries time out' do
......
......@@ -5,6 +5,7 @@ describe Clusters::Applications::Ingress do
include_examples 'cluster application core specs', :clusters_applications_ingress
include_examples 'cluster application status specs', :clusters_applications_ingress
include_examples 'cluster application helm specs', :clusters_applications_knative
before do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
......@@ -121,28 +122,5 @@ describe Clusters::Applications::Ingress do
expect(values).to include('stats')
expect(values).to include('podAnnotations')
end
context 'when the helm application does not have a ca_cert' do
before do
application.cluster.application_helm.ca_cert = nil
end
it 'should not include cert files' do
expect(subject[:'ca.pem']).not_to be_present
expect(subject[:'cert.pem']).not_to be_present
expect(subject[:'key.pem']).not_to be_present
end
end
it 'should include cert files' do
expect(subject[:'ca.pem']).to be_present
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
expect(subject[:'cert.pem']).to be_present
expect(subject[:'key.pem']).to be_present
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
expect(cert.not_after).to be < 60.minutes.from_now
end
end
end
......@@ -2,6 +2,7 @@ require 'rails_helper'
describe Clusters::Applications::Jupyter do
include_examples 'cluster application core specs', :clusters_applications_jupyter
include_examples 'cluster application helm specs', :clusters_applications_knative
it { is_expected.to belong_to(:oauth_application) }
......@@ -79,29 +80,6 @@ describe Clusters::Applications::Jupyter do
subject { application.files }
it 'should include cert files' do
expect(subject[:'ca.pem']).to be_present
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
expect(subject[:'cert.pem']).to be_present
expect(subject[:'key.pem']).to be_present
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
expect(cert.not_after).to be < 60.minutes.from_now
end
context 'when the helm application does not have a ca_cert' do
before do
application.cluster.application_helm.ca_cert = nil
end
it 'should not include cert files' do
expect(subject[:'ca.pem']).not_to be_present
expect(subject[:'cert.pem']).not_to be_present
expect(subject[:'key.pem']).not_to be_present
end
end
it 'should include valid values' do
expect(values).to include('ingress')
expect(values).to include('hub')
......
require 'rails_helper'
describe Clusters::Applications::Knative do
let(:knative) { create(:clusters_applications_knative) }
include_examples 'cluster application core specs', :clusters_applications_knative
include_examples 'cluster application status specs', :clusters_applications_knative
include_examples 'cluster application helm specs', :clusters_applications_knative
describe '.installed' do
subject { described_class.installed }
let!(:cluster) { create(:clusters_applications_knative, :installed) }
before do
create(:clusters_applications_knative, :errored)
end
it { is_expected.to contain_exactly(cluster) }
end
describe '#make_installing!' do
before do
application.make_installing!
end
context 'application install previously errored with older version' do
let(:application) { create(:clusters_applications_knative, :scheduled, version: '0.1.3') }
it 'updates the application version' do
expect(application.reload.version).to eq('0.1.3')
end
end
end
describe '#make_installed' do
subject { described_class.installed }
let!(:cluster) { create(:clusters_applications_knative, :installed) }
before do
create(:clusters_applications_knative, :errored)
end
it { is_expected.to contain_exactly(cluster) }
end
describe '#install_command' do
subject { knative.install_command }
it 'should be an instance of Helm::InstallCommand' do
expect(subject).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
end
it 'should be initialized with knative arguments' do
expect(subject.name).to eq('knative')
expect(subject.chart).to eq('knative/knative')
expect(subject.version).to eq('0.1.3')
expect(subject.files).to eq(knative.files)
end
end
describe '#files' do
let(:application) { knative }
let(:values) { subject[:'values.yaml'] }
subject { application.files }
it 'should include knative specific keys in the values.yaml file' do
expect(values).to include('domain')
end
end
describe 'validations' do
it { is_expected.to validate_presence_of(:hostname) }
end
end
......@@ -5,6 +5,7 @@ describe Clusters::Applications::Prometheus do
include_examples 'cluster application core specs', :clusters_applications_prometheus
include_examples 'cluster application status specs', :clusters_applications_prometheus
include_examples 'cluster application helm specs', :clusters_applications_knative
describe '.installed' do
subject { described_class.installed }
......@@ -187,29 +188,6 @@ describe Clusters::Applications::Prometheus do
subject { application.files }
it 'should include cert files' do
expect(subject[:'ca.pem']).to be_present
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
expect(subject[:'cert.pem']).to be_present
expect(subject[:'key.pem']).to be_present
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
expect(cert.not_after).to be < 60.minutes.from_now
end
context 'when the helm application does not have a ca_cert' do
before do
application.cluster.application_helm.ca_cert = nil
end
it 'should not include cert files' do
expect(subject[:'ca.pem']).not_to be_present
expect(subject[:'cert.pem']).not_to be_present
expect(subject[:'key.pem']).not_to be_present
end
end
it 'should include prometheus valid values' do
expect(values).to include('alertmanager')
expect(values).to include('kubeStateMetrics')
......
......@@ -5,6 +5,7 @@ describe Clusters::Applications::Runner do
include_examples 'cluster application core specs', :clusters_applications_runner
include_examples 'cluster application status specs', :clusters_applications_runner
include_examples 'cluster application helm specs', :clusters_applications_knative
it { is_expected.to belong_to(:runner) }
......@@ -74,29 +75,6 @@ describe Clusters::Applications::Runner do
subject { application.files }
it 'should include cert files' do
expect(subject[:'ca.pem']).to be_present
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
expect(subject[:'cert.pem']).to be_present
expect(subject[:'key.pem']).to be_present
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
expect(cert.not_after).to be < 60.minutes.from_now
end
context 'when the helm application does not have a ca_cert' do
before do
application.cluster.application_helm.ca_cert = nil
end
it 'should not include cert files' do
expect(subject[:'ca.pem']).not_to be_present
expect(subject[:'cert.pem']).not_to be_present
expect(subject[:'key.pem']).not_to be_present
end
end
it 'should include runner valid values' do
expect(values).to include('concurrent')
expect(values).to include('checkInterval')
......
......@@ -314,9 +314,10 @@ describe Clusters::Cluster do
let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
let!(:runner) { create(:clusters_applications_runner, cluster: cluster) }
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) }
let!(:knative) { create(:clusters_applications_knative, cluster: cluster) }
it 'returns a list of created applications' do
is_expected.to contain_exactly(helm, ingress, prometheus, runner, jupyter)
is_expected.to contain_exactly(helm, ingress, prometheus, runner, jupyter, knative)
end
end
end
......
......@@ -67,5 +67,38 @@ describe Clusters::Applications::CreateService do
expect { subject }.to raise_error(Clusters::Applications::CreateService::InvalidApplicationError)
end
end
context 'knative application' do
let(:params) do
{
application: 'knative',
hostname: 'example.com'
}
end
before do
allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
end
it 'creates the application' do
expect do
subject
cluster.reload
end.to change(cluster, :application_knative)
end
it 'sets the hostname' do
expect(subject.hostname).to eq('example.com')
end
end
context 'invalid application' do
let(:params) { { application: 'non-existent' } }
it 'raises an error' do
expect { subject }.to raise_error(Clusters::Applications::CreateService::InvalidApplicationError)
end
end
end
end
......@@ -33,7 +33,7 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
expect(provider.endpoint).to eq(endpoint)
expect(platform.api_url).to eq(api_url)
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert).strip)
expect(platform.username).to eq(username)
expect(platform.password).to eq(password)
expect(platform.token).to eq(token)
......
shared_examples 'cluster application helm specs' do |application_name|
let(:application) { create(application_name) }
describe '#files' do
subject { application.files }
context 'when the helm application does not have a ca_cert' do
before do
application.cluster.application_helm.ca_cert = nil
end
it 'should not include cert files when there is no ca_cert entry' do
expect(subject).not_to include(:'ca.pem', :'cert.pem', :'key.pem')
end
end
it 'should include cert files when there is a ca_cert entry' do
expect(subject).to include(:'ca.pem', :'cert.pem', :'key.pem')
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
expect(cert.not_after).to be < 60.minutes.from_now
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