Commit 14d1a057 authored by Thong Kuah's avatar Thong Kuah

Merge branch 'cluster-environments-integration' into 'master'

Cluster environments BE/FE integration

See merge request gitlab-org/gitlab-ee!15515
parents 59022e4f b01275f1
......@@ -39,6 +39,7 @@ export default class Clusters {
updateKnativePath,
installPrometheusPath,
managePrometheusPath,
clusterEnvironmentsPath,
hasRbac,
clusterType,
clusterStatus,
......@@ -79,6 +80,7 @@ export default class Clusters {
installJupyterEndpoint: installJupyterPath,
installKnativeEndpoint: installKnativePath,
updateKnativeEndpoint: updateKnativePath,
clusterEnvironmentsEndpoint: clusterEnvironmentsPath,
});
this.installApplication = this.installApplication.bind(this);
......@@ -109,6 +111,10 @@ export default class Clusters {
this.initApplications(clusterType);
this.initEnvironments();
if (clusterEnvironmentsPath) {
this.fetchEnvironments();
}
this.updateContainer(null, this.store.state.status, this.store.state.statusReason);
this.addListeners();
......@@ -162,6 +168,7 @@ export default class Clusters {
render(createElement) {
return createElement(Environments, {
props: {
isFetching: this.state.fetchingEnvironments,
environments: this.state.environments,
environmentsHelpPath: this.state.environmentsHelpPath,
clustersHelpPath: this.state.clustersHelpPath,
......@@ -172,6 +179,18 @@ export default class Clusters {
});
}
fetchEnvironments() {
this.store.toggleFetchEnvironments(true);
this.service
.fetchClusterEnvironments()
.then(data => {
this.store.toggleFetchEnvironments(false);
this.store.updateEnvironments(data.data);
})
.catch(() => Clusters.handleError());
}
static initDismissableCallout() {
const callout = document.querySelector('.js-cluster-security-warning');
PersistentUserCallout.factory(callout);
......
......@@ -33,6 +33,10 @@ export default class ClusterService {
return axios.delete(this.appInstallEndpointMap[appId], params);
}
fetchClusterEnvironments() {
return axios.get(this.options.clusterEnvironmentsEndpoint);
}
static updateCluster(endpoint, data) {
return axios.put(endpoint, data);
}
......
......@@ -84,6 +84,7 @@ export default class ClusterStore {
},
},
environments: [],
fetchingEnvironments: false,
};
}
......@@ -206,6 +207,10 @@ export default class ClusterStore {
});
}
toggleFetchEnvironments(isFetching) {
this.state.fetchingEnvironments = isFetching;
}
updateEnvironments(environments = []) {
this.state.environments = environments.map(environment => ({
name: environment.name,
......@@ -215,7 +220,7 @@ export default class ClusterStore {
rolloutStatus: {
instances: environment.rollout_status ? environment.rollout_status.instances : [],
},
updatedAt: environment.updatedAt,
updatedAt: environment.updated_at,
}));
}
}
......@@ -16,6 +16,7 @@
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative),
cluster_environments_path: clusterable.environments_cluster_path(@cluster),
toggle_status: @cluster.enabled? ? 'true': 'false',
has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false',
cluster_type: @cluster.cluster_type,
......
# Cluster Environments **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/13392) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.3.
Cluster environments provide a consolidated view of which CI [environments](../../ci/environments.md) are
deployed to the Kubernetes cluster and it:
- Shows the project and the relevant environment related to the deployment.
- Displays the status of the pods for that environment.
## Overview
NOTE: **Note:**
Cluster environments are only available for
[group-level clusters](../group/clusters/index.md).
Support for [instance-level](../instance/clusters/index.md) clusters is
[planned](https://gitlab.com/gitlab-org/gitlab-ce/issues/63985).
With cluster environments, you can gain insight into:
- Which projects are deployed to the cluster.
- How many pods are in use for each project's environment.
- The CI job that was used to deploy to that environment.
![Cluster environments page](img/cluster_environments_table_v12_3.png)
Access to cluster environments is restricted to [group maintainers and
owners](../permissions.md#group-members-permissions)
## Usage
In order to:
- Track environments for the cluster, you must
[deploy to a Kubernetes cluster](../project/clusters/index.md#deploying-to-a-kubernetes-cluster)
successfully.
- Show pod usage correctly, you must
[enable Deploy Boards](../project/deploy_boards.md#enabling-deploy-boards).
Once you have successful deployments to your group-level cluster:
1. Navigate to your group's **Kubernetes** page.
1. Click on the **Environments** tab.
NOTE: **Note:**
Only successful deployments to the cluster is included in this page.
Non-cluster environments will not be included.
<script>
import { GlTable, GlLink, GlEmptyState } from '@gitlab/ui';
import { GlTable, GlLink, GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
......@@ -11,9 +11,14 @@ export default {
GlLink,
Icon,
TimeAgo,
GlLoadingIcon,
deploymentInstance: () => import('ee_component/vue_shared/components/deployment_instance.vue'),
},
props: {
isFetching: {
type: Boolean,
required: true,
},
environments: {
type: Array,
required: true,
......@@ -33,7 +38,7 @@ export default {
},
computed: {
isEmpty() {
return this.environments.length === 0;
return !this.isFetching && this.environments.length === 0;
},
tableEmptyStateText() {
const text = __(
......@@ -57,7 +62,9 @@ export default {
let podsInUse = 0;
this.environments.forEach(environment => {
if (this.hasInstances(environment.rolloutStatus)) {
podsInUse += environment.rolloutStatus.instances.length;
}
});
return podsInUse;
......@@ -76,6 +83,9 @@ export default {
},
];
},
methods: {
hasInstances: rolloutStatus => rolloutStatus.instances && rolloutStatus.instances.length,
},
};
</script>
......@@ -90,7 +100,12 @@ export default {
<div slot="description" v-html="tableEmptyStateText"></div>
</gl-empty-state>
<gl-table v-else :fields="$options.fields" :items="environments" head-variant="white">
<gl-table
v-if="!isFetching && !isEmpty"
:fields="$options.fields"
:items="environments"
head-variant="white"
>
<!-- column: Project -->
<template slot="project" slot-scope="data">
<a :href="`/${data.value.path_with_namespace}`">{{ data.value.name }}</a>
......@@ -112,7 +127,7 @@ export default {
</template>
<template slot="rolloutStatus" slot-scope="row">
<div v-if="row.item.rolloutStatus.instances.length" class="d-flex flex-wrap flex-row">
<div v-if="hasInstances(row.item.rolloutStatus)" class="d-flex flex-wrap flex-row">
<template v-for="(instance, i) in row.item.rolloutStatus.instances">
<deployment-instance
:key="i"
......@@ -141,5 +156,7 @@ export default {
<time-ago :time="data.value" />
</template>
</gl-table>
<gl-loading-icon v-if="isFetching" :size="2" class="mt-3" />
</div>
</template>
......@@ -14,6 +14,8 @@ module Clusters
expose :rollout_status, if: -> (*) { can_read_cluster_deployments? }, using: ::RolloutStatusEntity
expose :updated_at
private
alias_method :environment, :object
......
......@@ -7,7 +7,7 @@
%ul.nav-links.mobile-separator.nav.nav-tabs{ role: 'tablist' }
%li.nav-item{ role: 'presentation' }
%a.nav-link{ class: active_when(is_configure_active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'configure'}), id: 'group-cluster-configure-tab' }
%span= _('Configure')
%span= _('Configuration')
%li.nav-item{ role: 'presentation' }
%a.nav-link{ class: active_when(!is_configure_active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'environments'}), id: 'group-cluster-environments-tab' }
%span= _('Environments')
......
......@@ -19,8 +19,12 @@
]
},
"rollout_status": {
"$ref": "../rollout_status.json"
}
"oneOf": [
{ "type": "null" },
{ "$ref": "../rollout_status.json" }
]
},
"updated_at": { "type": "date" }
},
"additionalProperties": false
}
......@@ -17,6 +17,7 @@ describe('Environments', () => {
environmentsHelpPath: 'path/to/environments',
clustersHelpPath: 'path/to/clusters',
deployBoardsHelpPath: 'path/to/clusters',
isFetching: false,
};
wrapper = mount(Component, {
......
......@@ -22,24 +22,16 @@ describe Clusters::EnvironmentEntity do
subject { described_class.new(environment, request: request).as_json }
it 'exposes project' do
expect(subject).to include(:project)
end
it 'exposes last_deployment' do
expect(subject).to include(:last_deployment)
end
it 'exposes environment_path' do
expect(subject).to include(:environment_path)
end
context 'deploy board available' do
before do
allow(group).to receive(:feature_available?).and_call_original
allow(group).to receive(:feature_available?).with(:cluster_deployments).and_return(true)
end
it 'matches expected schema' do
expect(subject.with_indifferent_access).to match_schema('clusters/environment', dir: 'ee')
end
it 'exposes rollout_status' do
expect(subject).to include(:rollout_status)
end
......@@ -50,6 +42,10 @@ describe Clusters::EnvironmentEntity do
allow(group).to receive(:feature_available?).with(:cluster_deployments).and_return(false)
end
it 'matches expected schema' do
expect(subject.with_indifferent_access).to match_schema('clusters/environment', dir: 'ee')
end
it 'does not expose rollout_status' do
expect(subject).not_to include(:rollout_status)
end
......
......@@ -3918,7 +3918,7 @@ msgstr ""
msgid "Confidentiality"
msgstr ""
msgid "Configure"
msgid "Configuration"
msgstr ""
msgid "Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
......
......@@ -152,6 +152,7 @@ describe('Clusters Store', () => {
},
},
environments: [],
fetchingEnvironments: false,
});
});
......
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