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>2020-12-17 14:59:07 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 14:59:07 +0300
commit8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch)
tree544930fb309b30317ae9797a9683768705d664c4 /app/assets/javascripts/terraform
parent4b1de649d0168371549608993deac953eb692019 (diff)
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'app/assets/javascripts/terraform')
-rw-r--r--app/assets/javascripts/terraform/components/states_table.vue122
-rw-r--r--app/assets/javascripts/terraform/components/states_table_actions.vue192
-rw-r--r--app/assets/javascripts/terraform/components/terraform_list.vue58
-rw-r--r--app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql17
-rw-r--r--app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql5
-rw-r--r--app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql5
-rw-r--r--app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql5
-rw-r--r--app/assets/javascripts/terraform/index.js1
8 files changed, 358 insertions, 47 deletions
diff --git a/app/assets/javascripts/terraform/components/states_table.vue b/app/assets/javascripts/terraform/components/states_table.vue
index 2e4c18c5a5b..d0d49233334 100644
--- a/app/assets/javascripts/terraform/components/states_table.vue
+++ b/app/assets/javascripts/terraform/components/states_table.vue
@@ -1,16 +1,22 @@
<script>
-import { GlBadge, GlIcon, GlSprintf, GlTable, GlTooltip } from '@gitlab/ui';
+import { GlBadge, GlIcon, GlLink, GlSprintf, GlTable, GlTooltip } from '@gitlab/ui';
import { s__ } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
+import StateActions from './states_table_actions.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
export default {
components: {
+ CiBadge,
GlBadge,
GlIcon,
+ GlLink,
GlSprintf,
GlTable,
GlTooltip,
+ StateActions,
TimeAgoTooltip,
},
mixins: [timeagoMixin],
@@ -19,28 +25,73 @@ export default {
required: true,
type: Array,
},
+ terraformAdmin: {
+ required: false,
+ type: Boolean,
+ default: false,
+ },
},
computed: {
fields() {
- return [
+ const columns = [
{
key: 'name',
- thClass: 'gl-display-none',
+ label: this.$options.i18n.name,
+ },
+ {
+ key: 'pipeline',
+ label: this.$options.i18n.pipeline,
},
{
key: 'updated',
- thClass: 'gl-display-none',
- tdClass: 'gl-text-right',
+ label: this.$options.i18n.details,
},
];
+
+ if (this.terraformAdmin) {
+ columns.push({
+ key: 'actions',
+ label: this.$options.i18n.actions,
+ thClass: 'gl-w-12',
+ tdClass: 'gl-text-right',
+ });
+ }
+
+ return columns;
},
},
+ i18n: {
+ actions: s__('Terraform|Actions'),
+ details: s__('Terraform|Details'),
+ jobStatus: s__('Terraform|Job status'),
+ locked: s__('Terraform|Locked'),
+ lockedByUser: s__('Terraform|Locked by %{user} %{timeAgo}'),
+ name: s__('Terraform|Name'),
+ pipeline: s__('Terraform|Pipeline'),
+ unknownUser: s__('Terraform|Unknown User'),
+ updatedUser: s__('Terraform|%{user} updated %{timeAgo}'),
+ },
methods: {
createdByUserName(item) {
return item.latestVersion?.createdByUser?.name;
},
lockedByUserName(item) {
- return item.lockedByUser?.name || s__('Terraform|Unknown User');
+ return item.lockedByUser?.name || this.$options.i18n.unknownUser;
+ },
+ pipelineDetailedStatus(item) {
+ return item.latestVersion?.job?.detailedStatus;
+ },
+ pipelineID(item) {
+ let id = item.latestVersion?.job?.pipeline?.id;
+
+ if (id) {
+ id = getIdFromGraphQLId(id);
+ }
+
+ return id;
+ },
+ pipelinePath(item) {
+ return item.latestVersion?.job?.pipeline?.path;
},
updatedTime(item) {
return item.latestVersion?.updatedAt || item.updatedAt;
@@ -50,25 +101,34 @@ export default {
</script>
<template>
- <gl-table :items="states" :fields="fields" data-testid="terraform-states-table">
+ <gl-table
+ :items="states"
+ :fields="fields"
+ data-testid="terraform-states-table"
+ fixed
+ stacked="md"
+ >
<template #cell(name)="{ item }">
- <div class="gl-display-flex align-items-center" data-testid="terraform-states-table-name">
+ <div
+ class="gl-display-flex align-items-center gl-justify-content-end gl-justify-content-md-start"
+ data-testid="terraform-states-table-name"
+ >
<p class="gl-font-weight-bold gl-m-0 gl-text-gray-900">
{{ item.name }}
</p>
- <div v-if="item.lockedAt" id="terraformLockedBadgeContainer" class="gl-mx-2">
- <gl-badge id="terraformLockedBadge">
+ <div v-if="item.lockedAt" :id="`terraformLockedBadgeContainer${item.name}`" class="gl-mx-2">
+ <gl-badge :id="`terraformLockedBadge${item.name}`">
<gl-icon name="lock" />
- {{ s__('Terraform|Locked') }}
+ {{ $options.i18n.locked }}
</gl-badge>
<gl-tooltip
- container="terraformLockedBadgeContainer"
+ :container="`terraformLockedBadgeContainer${item.name}`"
+ :target="`terraformLockedBadge${item.name}`"
placement="right"
- target="terraformLockedBadge"
>
- <gl-sprintf :message="s__('Terraform|Locked by %{user} %{timeAgo}')">
+ <gl-sprintf :message="$options.i18n.lockedByUser">
<template #user>
{{ lockedByUserName(item) }}
</template>
@@ -82,9 +142,37 @@ export default {
</div>
</template>
+ <template #cell(pipeline)="{ item }">
+ <div data-testid="terraform-states-table-pipeline" class="gl-min-h-7">
+ <gl-link v-if="pipelineID(item)" :href="pipelinePath(item)">
+ #{{ pipelineID(item) }}
+ </gl-link>
+
+ <div
+ v-if="pipelineDetailedStatus(item)"
+ :id="`terraformJobStatusContainer${item.name}`"
+ class="gl-my-2"
+ >
+ <ci-badge
+ :id="`terraformJobStatus${item.name}`"
+ :status="pipelineDetailedStatus(item)"
+ class="gl-py-1"
+ />
+
+ <gl-tooltip
+ :container="`terraformJobStatusContainer${item.name}`"
+ :target="`terraformJobStatus${item.name}`"
+ placement="right"
+ >
+ {{ $options.i18n.jobStatus }}
+ </gl-tooltip>
+ </div>
+ </div>
+ </template>
+
<template #cell(updated)="{ item }">
<p class="gl-m-0" data-testid="terraform-states-table-updated">
- <gl-sprintf :message="s__('Terraform|%{user} updated %{timeAgo}')">
+ <gl-sprintf :message="$options.i18n.updatedUser">
<template #user>
<span v-if="item.latestVersion">
{{ createdByUserName(item) }}
@@ -97,5 +185,9 @@ export default {
</gl-sprintf>
</p>
</template>
+
+ <template v-if="terraformAdmin" #cell(actions)="{ item }">
+ <state-actions :state="item" />
+ </template>
</gl-table>
</template>
diff --git a/app/assets/javascripts/terraform/components/states_table_actions.vue b/app/assets/javascripts/terraform/components/states_table_actions.vue
new file mode 100644
index 00000000000..44b0713e544
--- /dev/null
+++ b/app/assets/javascripts/terraform/components/states_table_actions.vue
@@ -0,0 +1,192 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlFormGroup,
+ GlFormInput,
+ GlIcon,
+ GlModal,
+ GlSprintf,
+} from '@gitlab/ui';
+import { s__ } from '~/locale';
+import lockState from '../graphql/mutations/lock_state.mutation.graphql';
+import unlockState from '../graphql/mutations/unlock_state.mutation.graphql';
+import removeState from '../graphql/mutations/remove_state.mutation.graphql';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlFormGroup,
+ GlFormInput,
+ GlIcon,
+ GlModal,
+ GlSprintf,
+ },
+ props: {
+ state: {
+ required: true,
+ type: Object,
+ },
+ },
+ data() {
+ return {
+ loading: false,
+ showRemoveModal: false,
+ removeConfirmText: '',
+ };
+ },
+ i18n: {
+ downloadJSON: s__('Terraform|Download JSON'),
+ lock: s__('Terraform|Lock'),
+ modalBody: s__(
+ 'Terraform|You are about to remove the State file %{name}. This will permanently delete all the State versions and history. The infrastructure provisioned previously will remain intact, only the state file with all its versions are to be removed. This action is non-revertible.',
+ ),
+ modalCancel: s__('Terraform|Cancel'),
+ modalHeader: s__('Terraform|Are you sure you want to remove the Terraform State %{name}?'),
+ modalInputLabel: s__(
+ 'Terraform|To remove the State file and its versions, type %{name} to confirm:',
+ ),
+ modalRemove: s__('Terraform|Remove'),
+ remove: s__('Terraform|Remove state file and versions'),
+ unlock: s__('Terraform|Unlock'),
+ },
+ computed: {
+ cancelModalProps() {
+ return {
+ text: this.$options.i18n.modalCancel,
+ attributes: [],
+ };
+ },
+ disableModalSubmit() {
+ return this.removeConfirmText !== this.state.name;
+ },
+ primaryModalProps() {
+ return {
+ text: this.$options.i18n.modalRemove,
+ attributes: [{ disabled: this.disableModalSubmit }, { variant: 'danger' }],
+ };
+ },
+ },
+ methods: {
+ hideModal() {
+ this.showRemoveModal = false;
+ this.removeConfirmText = '';
+ },
+ lock() {
+ this.stateMutation(lockState);
+ },
+ unlock() {
+ this.stateMutation(unlockState);
+ },
+ remove() {
+ if (!this.disableModalSubmit) {
+ this.hideModal();
+ this.stateMutation(removeState);
+ }
+ },
+ stateMutation(mutation) {
+ this.loading = true;
+ this.$apollo
+ .mutate({
+ mutation,
+ variables: {
+ stateID: this.state.id,
+ },
+ refetchQueries: () => ['getStates'],
+ awaitRefetchQueries: true,
+ notifyOnNetworkStatusChange: true,
+ })
+ .catch(() => {})
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-dropdown
+ icon="ellipsis_v"
+ right
+ :data-testid="`terraform-state-actions-${state.name}`"
+ :disabled="loading"
+ toggle-class="gl-px-3! gl-shadow-none!"
+ >
+ <template #button-content>
+ <gl-icon class="gl-mr-0" name="ellipsis_v" />
+ </template>
+
+ <gl-dropdown-item
+ v-if="state.latestVersion"
+ data-testid="terraform-state-download"
+ :download="`${state.name}.json`"
+ :href="state.latestVersion.downloadPath"
+ >
+ {{ $options.i18n.downloadJSON }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-item v-if="state.lockedAt" data-testid="terraform-state-unlock" @click="unlock">
+ {{ $options.i18n.unlock }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-item v-else data-testid="terraform-state-lock" @click="lock">
+ {{ $options.i18n.lock }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-divider />
+
+ <gl-dropdown-item data-testid="terraform-state-remove" @click="showRemoveModal = true">
+ {{ $options.i18n.remove }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+
+ <gl-modal
+ :modal-id="`terraform-state-actions-remove-modal-${state.name}`"
+ :visible="showRemoveModal"
+ :action-primary="primaryModalProps"
+ :action-cancel="cancelModalProps"
+ @ok="remove"
+ @cancel="hideModal"
+ @close="hideModal"
+ @hide="hideModal"
+ >
+ <template #modal-title>
+ <gl-sprintf :message="$options.i18n.modalHeader">
+ <template #name>
+ <span>{{ state.name }}</span>
+ </template>
+ </gl-sprintf>
+ </template>
+
+ <p>
+ <gl-sprintf :message="$options.i18n.modalBody">
+ <template #name>
+ <span>{{ state.name }}</span>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <gl-form-group>
+ <template #label>
+ <gl-sprintf :message="$options.i18n.modalInputLabel">
+ <template #name>
+ <code>{{ state.name }}</code>
+ </template>
+ </gl-sprintf>
+ </template>
+ <gl-form-input
+ :id="`terraform-state-remove-input-${state.name}`"
+ ref="input"
+ v-model="removeConfirmText"
+ type="text"
+ @keyup.enter="remove"
+ />
+ </gl-form-group>
+ </gl-modal>
+ </div>
+</template>
diff --git a/app/assets/javascripts/terraform/components/terraform_list.vue b/app/assets/javascripts/terraform/components/terraform_list.vue
index f614bdc8d43..26a0bfe5fa5 100644
--- a/app/assets/javascripts/terraform/components/terraform_list.vue
+++ b/app/assets/javascripts/terraform/components/terraform_list.vue
@@ -15,13 +15,7 @@ export default {
...this.cursor,
};
},
- update: data => {
- return {
- count: data?.project?.terraformStates?.count,
- list: data?.project?.terraformStates?.nodes,
- pageInfo: data?.project?.terraformStates?.pageInfo,
- };
- },
+ update: data => data,
error() {
this.states = null;
},
@@ -46,6 +40,11 @@ export default {
required: true,
type: String,
},
+ terraformAdmin: {
+ required: false,
+ type: Boolean,
+ default: false,
+ },
},
data() {
return {
@@ -62,35 +61,34 @@ export default {
return this.$apollo.queries.states.loading;
},
pageInfo() {
- return this.states?.pageInfo || {};
+ return this.states?.project?.terraformStates?.pageInfo || {};
},
showPagination() {
return this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage;
},
statesCount() {
- return this.states?.count;
+ return this.states?.project?.terraformStates?.count;
},
statesList() {
- return this.states?.list;
+ return this.states?.project?.terraformStates?.nodes;
},
},
methods: {
- updatePagination(item) {
- if (item === this.pageInfo.endCursor) {
- this.cursor = {
- first: MAX_LIST_COUNT,
- after: item,
- last: null,
- before: null,
- };
- } else {
- this.cursor = {
- first: null,
- after: null,
- last: MAX_LIST_COUNT,
- before: item,
- };
- }
+ nextPage(item) {
+ this.cursor = {
+ first: MAX_LIST_COUNT,
+ after: item,
+ last: null,
+ before: null,
+ };
+ },
+ prevPage(item) {
+ this.cursor = {
+ first: null,
+ after: null,
+ last: MAX_LIST_COUNT,
+ before: item,
+ };
},
},
};
@@ -111,14 +109,10 @@ export default {
<div v-else-if="statesList">
<div v-if="statesCount">
- <states-table :states="statesList" />
+ <states-table :states="statesList" :terraform-admin="terraformAdmin" />
<div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5">
- <gl-keyset-pagination
- v-bind="pageInfo"
- @prev="updatePagination"
- @next="updatePagination"
- />
+ <gl-keyset-pagination v-bind="pageInfo" @prev="prevPage" @next="nextPage" />
</div>
</div>
diff --git a/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql b/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql
index c7e9700c696..70ba5c960be 100644
--- a/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql
+++ b/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql
@@ -1,9 +1,26 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
fragment StateVersion on TerraformStateVersion {
+ downloadPath
+ serial
updatedAt
createdByUser {
...User
}
+
+ job {
+ detailedStatus {
+ detailsPath
+ group
+ icon
+ label
+ text
+ }
+
+ pipeline {
+ id
+ path
+ }
+ }
}
diff --git a/app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql b/app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql
new file mode 100644
index 00000000000..aea0f8b025a
--- /dev/null
+++ b/app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql
@@ -0,0 +1,5 @@
+mutation lockState($stateID: TerraformStateID!) {
+ terraformStateLock(input: { id: $stateID }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql b/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql
new file mode 100644
index 00000000000..d85ebb9cea2
--- /dev/null
+++ b/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql
@@ -0,0 +1,5 @@
+mutation removeState($stateID: TerraformStateID!) {
+ terraformStateDelete(input: { id: $stateID }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql b/app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql
new file mode 100644
index 00000000000..1909fe95cf3
--- /dev/null
+++ b/app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql
@@ -0,0 +1,5 @@
+mutation unlockState($stateID: TerraformStateID!) {
+ terraformStateUnlock(input: { id: $stateID }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js
index 579d2d14023..e27a29433f3 100644
--- a/app/assets/javascripts/terraform/index.js
+++ b/app/assets/javascripts/terraform/index.js
@@ -24,6 +24,7 @@ export default () => {
props: {
emptyStateImage,
projectPath,
+ terraformAdmin: el.hasAttribute('data-terraform-admin'),
},
});
},