Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-03-18 23:02:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-18 23:02:30 +0300
commit41fe97390ceddf945f3d967b8fdb3de4c66b7dea (patch)
tree9c8d89a8624828992f06d892cd2f43818ff5dcc8 /app/assets/javascripts/clusters
parent0804d2dc31052fb45a1efecedc8e06ce9bc32862 (diff)
Add latest changes from gitlab-org/gitlab@14-9-stable-eev14.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/clusters')
-rw-r--r--app/assets/javascripts/clusters/agents/components/create_token_button.vue246
-rw-r--r--app/assets/javascripts/clusters/agents/components/show.vue2
-rw-r--r--app/assets/javascripts/clusters/agents/components/token_table.vue41
-rw-r--r--app/assets/javascripts/clusters/agents/constants.js7
-rw-r--r--app/assets/javascripts/clusters/agents/graphql/cache_update.js24
-rw-r--r--app/assets/javascripts/clusters/agents/graphql/mutations/create_new_agent_token.mutation.graphql11
-rw-r--r--app/assets/javascripts/clusters/agents/index.js5
-rw-r--r--app/assets/javascripts/clusters/components/new_cluster.vue6
8 files changed, 318 insertions, 24 deletions
diff --git a/app/assets/javascripts/clusters/agents/components/create_token_button.vue b/app/assets/javascripts/clusters/agents/components/create_token_button.vue
new file mode 100644
index 00000000000..3e1a8994fb8
--- /dev/null
+++ b/app/assets/javascripts/clusters/agents/components/create_token_button.vue
@@ -0,0 +1,246 @@
+<script>
+import {
+ GlButton,
+ GlModalDirective,
+ GlTooltip,
+ GlModal,
+ GlFormGroup,
+ GlFormInput,
+ GlFormTextarea,
+ GlAlert,
+} from '@gitlab/ui';
+import { s__, __ } from '~/locale';
+import Tracking from '~/tracking';
+import AgentToken from '~/clusters_list/components/agent_token.vue';
+import {
+ CREATE_TOKEN_MODAL,
+ EVENT_LABEL_MODAL,
+ EVENT_ACTIONS_OPEN,
+ EVENT_ACTIONS_CLICK,
+ TOKEN_NAME_LIMIT,
+ TOKEN_STATUS_ACTIVE,
+} from '../constants';
+import createNewAgentToken from '../graphql/mutations/create_new_agent_token.mutation.graphql';
+import getClusterAgentQuery from '../graphql/queries/get_cluster_agent.query.graphql';
+import { addAgentTokenToStore } from '../graphql/cache_update';
+
+const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_MODAL });
+
+export default {
+ components: {
+ AgentToken,
+ GlButton,
+ GlTooltip,
+ GlModal,
+ GlFormGroup,
+ GlFormInput,
+ GlFormTextarea,
+ GlAlert,
+ },
+ directives: {
+ GlModalDirective,
+ },
+ mixins: [trackingMixin],
+ inject: ['agentName', 'projectPath', 'canAdminCluster'],
+ props: {
+ clusterAgentId: {
+ required: true,
+ type: String,
+ },
+ cursor: {
+ required: true,
+ type: Object,
+ },
+ },
+ modalId: CREATE_TOKEN_MODAL,
+ EVENT_ACTIONS_OPEN,
+ EVENT_ACTIONS_CLICK,
+ EVENT_LABEL_MODAL,
+ TOKEN_NAME_LIMIT,
+ i18n: {
+ createTokenButton: s__('ClusterAgents|Create token'),
+ modalTitle: s__('ClusterAgents|Create agent access token'),
+ unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'),
+ errorTitle: s__('ClusterAgents|Failed to create a token'),
+ dropdownDisabledHint: s__(
+ 'ClusterAgents|Requires a Maintainer or greater role to perform these actions',
+ ),
+ modalCancel: __('Cancel'),
+ modalClose: __('Close'),
+ tokenNameLabel: __('Name'),
+ tokenDescriptionLabel: __('Description (optional)'),
+ },
+ data() {
+ return {
+ token: {
+ name: null,
+ description: null,
+ },
+ agentToken: null,
+ error: null,
+ loading: false,
+ variables: {
+ agentName: this.agentName,
+ projectPath: this.projectPath,
+ tokenStatus: TOKEN_STATUS_ACTIVE,
+ ...this.cursor,
+ },
+ };
+ },
+ computed: {
+ modalBtnDisabled() {
+ return this.loading || !this.hasTokenName;
+ },
+ hasTokenName() {
+ return Boolean(this.token.name?.length);
+ },
+ },
+ methods: {
+ async createToken() {
+ this.loading = true;
+ this.error = null;
+
+ try {
+ const { errors: tokenErrors, secret } = await this.createAgentTokenMutation();
+
+ if (tokenErrors?.length > 0) {
+ throw new Error(tokenErrors[0]);
+ }
+
+ this.agentToken = secret;
+ } catch (error) {
+ if (error) {
+ this.error = error.message;
+ } else {
+ this.error = this.$options.i18n.unknownError;
+ }
+ } finally {
+ this.loading = false;
+ }
+ },
+ resetModal() {
+ this.agentToken = null;
+ this.token.name = null;
+ this.token.description = null;
+ this.error = null;
+ },
+ closeModal() {
+ this.$refs.modal.hide();
+ },
+ createAgentTokenMutation() {
+ return this.$apollo
+ .mutate({
+ mutation: createNewAgentToken,
+ variables: {
+ input: {
+ clusterAgentId: this.clusterAgentId,
+ name: this.token.name,
+ description: this.token.description,
+ },
+ },
+ update: (store, { data: { clusterAgentTokenCreate } }) => {
+ addAgentTokenToStore(
+ store,
+ clusterAgentTokenCreate,
+ getClusterAgentQuery,
+ this.variables,
+ );
+ },
+ })
+ .then(({ data: { clusterAgentTokenCreate } }) => clusterAgentTokenCreate);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div ref="addToken" class="gl-display-inline-block">
+ <gl-button
+ v-gl-modal-directive="$options.modalId"
+ :disabled="!canAdminCluster"
+ category="primary"
+ variant="confirm"
+ >{{ $options.i18n.createTokenButton }}
+ </gl-button>
+
+ <gl-tooltip
+ v-if="!canAdminCluster"
+ :target="() => $refs.addToken"
+ :title="$options.i18n.dropdownDisabledHint"
+ />
+ </div>
+
+ <gl-modal
+ ref="modal"
+ :modal-id="$options.modalId"
+ :title="$options.i18n.modalTitle"
+ static
+ lazy
+ @hidden="resetModal"
+ @show="track($options.EVENT_ACTIONS_OPEN)"
+ >
+ <gl-alert
+ v-if="error"
+ :title="$options.i18n.errorTitle"
+ :dismissible="false"
+ variant="danger"
+ class="gl-mb-5"
+ >
+ {{ error }}
+ </gl-alert>
+
+ <template v-if="!agentToken">
+ <gl-form-group :label="$options.i18n.tokenNameLabel">
+ <gl-form-input
+ v-model="token.name"
+ :max-length="$options.TOKEN_NAME_LIMIT"
+ :disabled="loading"
+ required
+ />
+ </gl-form-group>
+
+ <gl-form-group :label="$options.i18n.tokenDescriptionLabel">
+ <gl-form-textarea v-model="token.description" :disabled="loading" name="description" />
+ </gl-form-group>
+ </template>
+
+ <agent-token v-else :agent-token="agentToken" :modal-id="$options.modalId" />
+
+ <template #modal-footer>
+ <gl-button
+ v-if="!agentToken && !loading"
+ :data-track-action="$options.EVENT_ACTIONS_CLICK"
+ :data-track-label="$options.EVENT_LABEL_MODAL"
+ data-track-property="close"
+ data-testid="agent-token-close-button"
+ @click="closeModal"
+ >{{ $options.i18n.modalCancel }}
+ </gl-button>
+
+ <gl-button
+ v-if="!agentToken"
+ :disabled="modalBtnDisabled"
+ :loading="loading"
+ :data-track-action="$options.EVENT_ACTIONS_CLICK"
+ :data-track-label="$options.EVENT_LABEL_MODAL"
+ data-track-property="create-token"
+ variant="confirm"
+ type="submit"
+ @click="createToken"
+ >{{ $options.i18n.createTokenButton }}
+ </gl-button>
+
+ <gl-button
+ v-else
+ :data-track-action="$options.EVENT_ACTIONS_CLICK"
+ :data-track-label="$options.EVENT_LABEL_MODAL"
+ data-track-property="close"
+ variant="confirm"
+ @click="closeModal"
+ >{{ $options.i18n.modalClose }}
+ </gl-button>
+ </template>
+ </gl-modal>
+ </div>
+</template>
diff --git a/app/assets/javascripts/clusters/agents/components/show.vue b/app/assets/javascripts/clusters/agents/components/show.vue
index 63f068a9327..5df3e0811a5 100644
--- a/app/assets/javascripts/clusters/agents/components/show.vue
+++ b/app/assets/javascripts/clusters/agents/components/show.vue
@@ -143,7 +143,7 @@ export default {
<gl-loading-icon v-if="isLoading" size="md" class="gl-m-3" />
<div v-else>
- <token-table :tokens="tokens" />
+ <token-table :tokens="tokens" :cluster-agent-id="clusterAgent.id" :cursor="cursor" />
<div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5">
<gl-keyset-pagination v-bind="tokenPageInfo" @prev="prevPage" @next="nextPage" />
diff --git a/app/assets/javascripts/clusters/agents/components/token_table.vue b/app/assets/javascripts/clusters/agents/components/token_table.vue
index 019fac531d1..fbb39c28d78 100644
--- a/app/assets/javascripts/clusters/agents/components/token_table.vue
+++ b/app/assets/javascripts/clusters/agents/components/token_table.vue
@@ -1,17 +1,17 @@
<script>
-import { GlEmptyState, GlLink, GlTable, GlTooltip, GlTruncate } from '@gitlab/ui';
-import { helpPagePath } from '~/helpers/help_page_helper';
+import { GlEmptyState, GlTable, GlTooltip, GlTruncate } from '@gitlab/ui';
import { s__ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import CreateTokenButton from './create_token_button.vue';
export default {
components: {
GlEmptyState,
- GlLink,
GlTable,
GlTooltip,
GlTruncate,
TimeAgoTooltip,
+ CreateTokenButton,
},
i18n: {
createdBy: s__('ClusterAgents|Created by'),
@@ -19,7 +19,6 @@ export default {
dateCreated: s__('ClusterAgents|Date created'),
description: s__('ClusterAgents|Description'),
lastUsed: s__('ClusterAgents|Last contact'),
- learnMore: s__('ClusterAgents|Learn how to create an agent access token'),
name: s__('ClusterAgents|Name'),
neverUsed: s__('ClusterAgents|Never'),
noTokens: s__('ClusterAgents|This agent has no tokens'),
@@ -30,6 +29,14 @@ export default {
required: true,
type: Array,
},
+ clusterAgentId: {
+ required: true,
+ type: String,
+ },
+ cursor: {
+ required: true,
+ type: Object,
+ },
},
computed: {
fields() {
@@ -61,11 +68,6 @@ export default {
},
];
},
- learnMoreUrl() {
- return helpPagePath('user/clusters/agent/install/index', {
- anchor: 'register-an-agent-with-gitlab',
- });
- },
},
methods: {
createdByName(token) {
@@ -77,11 +79,11 @@ export default {
<template>
<div v-if="tokens.length">
- <div class="gl-text-right gl-my-5">
- <gl-link target="_blank" :href="learnMoreUrl">
- {{ $options.i18n.learnMore }}
- </gl-link>
- </div>
+ <create-token-button
+ class="gl-text-right gl-my-5"
+ :cluster-agent-id="clusterAgentId"
+ :cursor="cursor"
+ />
<gl-table
:items="tokens"
@@ -120,10 +122,9 @@ export default {
</gl-table>
</div>
- <gl-empty-state
- v-else
- :title="$options.i18n.noTokens"
- :primary-button-link="learnMoreUrl"
- :primary-button-text="$options.i18n.learnMore"
- />
+ <gl-empty-state v-else :title="$options.i18n.noTokens">
+ <template #actions>
+ <create-token-button :cluster-agent-id="clusterAgentId" :cursor="cursor" />
+ </template>
+ </gl-empty-state>
</template>
diff --git a/app/assets/javascripts/clusters/agents/constants.js b/app/assets/javascripts/clusters/agents/constants.js
index 98d4707b4de..50d8f5e9e40 100644
--- a/app/assets/javascripts/clusters/agents/constants.js
+++ b/app/assets/javascripts/clusters/agents/constants.js
@@ -37,3 +37,10 @@ export const EVENT_DETAILS = {
export const DEFAULT_ICON = 'token';
export const TOKEN_STATUS_ACTIVE = 'ACTIVE';
+
+export const CREATE_TOKEN_MODAL = 'create-token';
+export const EVENT_LABEL_MODAL = 'agent_token_creation_modal';
+export const EVENT_ACTIONS_OPEN = 'open_modal';
+export const EVENT_ACTIONS_CLICK = 'click_button';
+
+export const TOKEN_NAME_LIMIT = 255;
diff --git a/app/assets/javascripts/clusters/agents/graphql/cache_update.js b/app/assets/javascripts/clusters/agents/graphql/cache_update.js
new file mode 100644
index 00000000000..0219c4150eb
--- /dev/null
+++ b/app/assets/javascripts/clusters/agents/graphql/cache_update.js
@@ -0,0 +1,24 @@
+import produce from 'immer';
+
+export const hasErrors = ({ errors = [] }) => errors?.length;
+
+export function addAgentTokenToStore(store, clusterAgentTokenCreate, query, variables) {
+ if (!hasErrors(clusterAgentTokenCreate)) {
+ const { token } = clusterAgentTokenCreate;
+ const sourceData = store.readQuery({
+ query,
+ variables,
+ });
+
+ const data = produce(sourceData, (draftData) => {
+ draftData.project.clusterAgent.tokens.nodes.unshift(token);
+ draftData.project.clusterAgent.tokens.count += 1;
+ });
+
+ store.writeQuery({
+ query,
+ variables,
+ data,
+ });
+ }
+}
diff --git a/app/assets/javascripts/clusters/agents/graphql/mutations/create_new_agent_token.mutation.graphql b/app/assets/javascripts/clusters/agents/graphql/mutations/create_new_agent_token.mutation.graphql
new file mode 100644
index 00000000000..4a61263ba70
--- /dev/null
+++ b/app/assets/javascripts/clusters/agents/graphql/mutations/create_new_agent_token.mutation.graphql
@@ -0,0 +1,11 @@
+#import "../fragments/cluster_agent_token.fragment.graphql"
+
+mutation createNewAgentToken($input: ClusterAgentTokenCreateInput!) {
+ clusterAgentTokenCreate(input: $input) {
+ secret
+ token {
+ ...Token
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/clusters/agents/index.js b/app/assets/javascripts/clusters/agents/index.js
index ba7b3edba72..8a447f57f00 100644
--- a/app/assets/javascripts/clusters/agents/index.js
+++ b/app/assets/javascripts/clusters/agents/index.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
import AgentShowPage from 'ee_else_ce/clusters/agents/components/show.vue';
import apolloProvider from './graphql/provider';
import createRouter from './router';
@@ -16,6 +17,8 @@ export default () => {
canAdminVulnerability,
emptyStateSvgPath,
projectPath,
+ kasAddress,
+ canAdminCluster,
} = el.dataset;
return new Vue({
@@ -28,6 +31,8 @@ export default () => {
canAdminVulnerability,
emptyStateSvgPath,
projectPath,
+ kasAddress,
+ canAdminCluster: parseBoolean(canAdminCluster),
},
render(createElement) {
return createElement(AgentShowPage);
diff --git a/app/assets/javascripts/clusters/components/new_cluster.vue b/app/assets/javascripts/clusters/components/new_cluster.vue
index 2e74ad073c5..8f3e2916270 100644
--- a/app/assets/javascripts/clusters/components/new_cluster.vue
+++ b/app/assets/javascripts/clusters/components/new_cluster.vue
@@ -5,9 +5,9 @@ import { s__ } from '~/locale';
export default {
i18n: {
- title: s__('ClusterIntegration|Enter the details for your Kubernetes cluster'),
+ title: s__('ClusterIntegration|Enter your Kubernetes cluster certificate details'),
information: s__(
- 'ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{linkStart}documentation%{linkEnd} on Kubernetes',
+ 'ClusterIntegration|Enter details about your cluster. %{linkStart}How do I use a certificate to connect to my cluster?%{linkEnd}',
),
},
components: {
@@ -21,7 +21,7 @@ export default {
</script>
<template>
- <div>
+ <div class="gl-pt-4">
<h4>{{ $options.i18n.title }}</h4>
<p>
<gl-sprintf :message="$options.i18n.information">