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>2023-08-07 21:10:22 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-07 21:10:22 +0300
commit4d18bba787186aeb5bf8a0463fd145fae48b3234 (patch)
treedaa26f7daa8bf303ef0cfcc1bfe68d13eae74537 /app/assets
parent33c86930e0a657e1519082a9a00faae260a44882 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/boards/components/board_app.vue11
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue12
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue19
-rw-r--r--app/assets/javascripts/boards/components/config_toggle.vue2
-rw-r--r--app/assets/javascripts/boards/graphql/group_boards.query.graphql8
-rw-r--r--app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql8
-rw-r--r--app/assets/javascripts/boards/graphql/project_boards.query.graphql8
-rw-r--r--app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql8
-rw-r--r--app/assets/javascripts/environments/components/kubernetes_overview.vue11
-rw-r--r--app/assets/javascripts/environments/components/kubernetes_status_bar.vue130
-rw-r--r--app/assets/javascripts/environments/components/new_environment_item.vue6
-rw-r--r--app/assets/javascripts/environments/constants.js36
-rw-r--r--app/assets/javascripts/environments/graphql/client.js17
-rw-r--r--app/assets/javascripts/environments/graphql/queries/flux_helm_release_status.query.graphql14
-rw-r--r--app/assets/javascripts/environments/graphql/queries/flux_kustomization_status.query.graphql14
-rw-r--r--app/assets/javascripts/environments/graphql/resolvers.js49
-rw-r--r--app/assets/javascripts/environments/graphql/typedefs.graphql15
-rw-r--r--app/assets/javascripts/group_settings/components/shared_runners_form.vue81
-rw-r--r--app/assets/javascripts/group_settings/mount_shared_runners.js8
-rw-r--r--app/assets/javascripts/token_access/components/inbound_token_access.vue65
-rw-r--r--app/assets/javascripts/token_access/components/outbound_token_access.vue47
-rw-r--r--app/assets/javascripts/token_access/components/token_access_app.vue4
23 files changed, 465 insertions, 112 deletions
diff --git a/app/assets/javascripts/boards/components/board_app.vue b/app/assets/javascripts/boards/components/board_app.vue
index 7d6bcfbe200..43b33acc045 100644
--- a/app/assets/javascripts/boards/components/board_app.vue
+++ b/app/assets/javascripts/boards/components/board_app.vue
@@ -6,8 +6,8 @@ import BoardContent from '~/boards/components/board_content.vue';
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
import BoardTopBar from '~/boards/components/board_top_bar.vue';
import eventHub from '~/boards/eventhub';
-import { listsQuery } from 'ee_else_ce/boards/constants';
-import { formatBoardLists } from 'ee_else_ce/boards/boards_util';
+import { listsQuery, FilterFields } from 'ee_else_ce/boards/constants';
+import { formatBoardLists, filterVariables, FiltersInfo } from 'ee_else_ce/boards/boards_util';
import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql';
import errorQuery from '../graphql/client/error.query.graphql';
import { setError } from '../graphql/cache_updates';
@@ -137,7 +137,12 @@ export default {
setFilters(filters) {
const filterParams = { ...filters };
if (filterParams.groupBy) delete filterParams.groupBy;
- this.filterParams = filterParams;
+ this.filterParams = filterVariables({
+ filters: filterParams,
+ issuableType: this.issuableType,
+ filterInfo: FiltersInfo,
+ filterFields: FilterFields,
+ });
},
},
};
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 18495f285da..b514330fcdd 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -113,8 +113,8 @@ export default {
this.$apollo.mutate({
mutation: setActiveBoardItemMutation,
variables: {
- boardItem: this.item,
- isIssue: this.isIssueBoard,
+ boardItem: this.isActive ? null : this.item,
+ isIssue: this.isActive ? undefined : this.isIssueBoard,
},
});
},
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index f5e1e51cd0c..682d380cc20 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -8,6 +8,7 @@ import { defaultSortableOptions } from '~/sortable/constants';
import { sortableStart, sortableEnd } from '~/sortable/utils';
import Tracking from '~/tracking';
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
+import setActiveBoardItemMutation from 'ee_else_ce/boards/graphql/client/set_active_board_item.mutation.graphql';
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
@@ -49,6 +50,7 @@ export default {
mixins: [Tracking.mixin(), glFeatureFlagMixin()],
inject: [
'isEpicBoard',
+ 'isIssueBoard',
'isGroupBoard',
'disabled',
'fullPath',
@@ -578,6 +580,7 @@ export default {
async addListItem(input) {
this.toggleForm();
this.addItemToListInProgress = true;
+ let issuable;
try {
await this.$apollo.mutate({
mutation: listIssuablesQueries[this.issuableType].createMutation,
@@ -586,7 +589,7 @@ export default {
withColor: this.isEpicBoard && this.glFeatures.epicColorHighlight,
},
update: (cache, { data: { createIssuable } }) => {
- const { issuable } = createIssuable;
+ issuable = createIssuable.issuable;
addItemToList({
query: listIssuablesQueries[this.issuableType].query,
variables: { ...this.listQueryVariables, id: this.currentList.id },
@@ -626,6 +629,13 @@ export default {
});
} finally {
this.addItemToListInProgress = false;
+ this.$apollo.mutate({
+ mutation: setActiveBoardItemMutation,
+ variables: {
+ boardItem: issuable,
+ isIssue: this.isIssueBoard,
+ },
+ });
}
},
},
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index 2f87e3fd44f..ae12ca5feed 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -94,9 +94,12 @@ export default {
parentType() {
return this.boardType;
},
- boardQuery() {
+ issueBoardsQuery() {
return this.isGroupBoard ? groupBoardsQuery : projectBoardsQuery;
},
+ boardsQuery() {
+ return this.issueBoardsQuery;
+ },
loading() {
return this.loadingRecentBoards || this.loadingBoards;
},
@@ -161,7 +164,7 @@ export default {
if (!data?.[this.parentType]) {
return [];
}
- return data[this.parentType][boardType].edges.map(({ node }) => ({
+ return data[this.parentType][boardType].nodes.map((node) => ({
id: getIdFromGraphQLId(node.id),
name: node.name,
}));
@@ -178,7 +181,7 @@ export default {
variables() {
return { fullPath: this.fullPath };
},
- query: this.boardQuery,
+ query: this.boardsQuery,
update: (data) => this.boardUpdate(data, 'boards'),
watchLoading: (isLoading) => {
this.loadingBoards = isLoading;
@@ -217,19 +220,19 @@ export default {
const { defaultClient: store } = this.$apollo.provider.clients;
const sourceData = store.readQuery({
- query: this.boardQuery,
+ query: this.boardsQuery,
variables: { fullPath: this.fullPath },
});
const newData = produce(sourceData, (draftState) => {
- draftState[this.parentType].boards.edges = [
- ...draftState[this.parentType].boards.edges,
- { node: board },
+ draftState[this.parentType].boards.nodes = [
+ ...draftState[this.parentType].boards.nodes,
+ { ...board },
];
});
store.writeQuery({
- query: this.boardQuery,
+ query: this.boardsQuery,
variables: { fullPath: this.fullPath },
data: newData,
});
diff --git a/app/assets/javascripts/boards/components/config_toggle.vue b/app/assets/javascripts/boards/components/config_toggle.vue
index dd3b9472879..b6ee3871b4f 100644
--- a/app/assets/javascripts/boards/components/config_toggle.vue
+++ b/app/assets/javascripts/boards/components/config_toggle.vue
@@ -29,7 +29,7 @@ export default {
return this.canAdminList ? s__('Boards|Edit board') : s__('Boards|View scope');
},
tooltipTitle() {
- return this.hasScope ? __("This board's scope is reduced") : '';
+ return this.hasScope || this.boardHasScope ? __("This board's scope is reduced") : '';
},
},
methods: {
diff --git a/app/assets/javascripts/boards/graphql/group_boards.query.graphql b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
index ce9f7bbfd2a..0fb5748abfc 100644
--- a/app/assets/javascripts/boards/graphql/group_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
@@ -2,11 +2,9 @@ query group_boards($fullPath: ID!) {
group(fullPath: $fullPath) {
id
boards {
- edges {
- node {
- id
- name
- }
+ nodes {
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql b/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql
index b9fe778d4d4..9dbf4528cec 100644
--- a/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql
@@ -2,11 +2,9 @@ query group_recent_boards($fullPath: ID!) {
group(fullPath: $fullPath) {
id
recentIssueBoards {
- edges {
- node {
- id
- name
- }
+ nodes {
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/boards/graphql/project_boards.query.graphql b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
index 770c246a95b..97a298db246 100644
--- a/app/assets/javascripts/boards/graphql/project_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
@@ -2,11 +2,9 @@ query project_boards($fullPath: ID!) {
project(fullPath: $fullPath) {
id
boards {
- edges {
- node {
- id
- name
- }
+ nodes {
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql b/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql
index c633107a409..0d3a8616603 100644
--- a/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql
@@ -2,11 +2,9 @@ query project_recent_boards($fullPath: ID!) {
project(fullPath: $fullPath) {
id
recentIssueBoards {
- edges {
- node {
- id
- name
- }
+ nodes {
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/environments/components/kubernetes_overview.vue b/app/assets/javascripts/environments/components/kubernetes_overview.vue
index a1efeaac359..8c30520ebec 100644
--- a/app/assets/javascripts/environments/components/kubernetes_overview.vue
+++ b/app/assets/javascripts/environments/components/kubernetes_overview.vue
@@ -24,6 +24,10 @@ export default {
required: true,
type: Object,
},
+ environmentName: {
+ required: true,
+ type: String,
+ },
namespace: {
required: false,
type: String,
@@ -96,7 +100,12 @@ export default {
</p>
<gl-collapse :visible="isVisible" class="gl-md-pl-7 gl-md-pr-5 gl-mt-4">
<template v-if="isVisible">
- <kubernetes-status-bar :cluster-health-status="clusterHealthStatus" class="gl-mb-4" />
+ <kubernetes-status-bar
+ :cluster-health-status="clusterHealthStatus"
+ :configuration="k8sAccessConfiguration"
+ :namespace="namespace"
+ :environment-name="environmentName"
+ class="gl-mb-3" />
<kubernetes-agent-info :cluster-agent="clusterAgent" class="gl-mb-5" />
<gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mb-5">
diff --git a/app/assets/javascripts/environments/components/kubernetes_status_bar.vue b/app/assets/javascripts/environments/components/kubernetes_status_bar.vue
index 94cd7438e46..b8859e65e38 100644
--- a/app/assets/javascripts/environments/components/kubernetes_status_bar.vue
+++ b/app/assets/javascripts/environments/components/kubernetes_status_bar.vue
@@ -1,7 +1,9 @@
<script>
import { GlLoadingIcon, GlBadge } from '@gitlab/ui';
import { s__ } from '~/locale';
-import { HEALTH_BADGES } from '../constants';
+import { HEALTH_BADGES, SYNC_STATUS_BADGES, STATUS_TRUE, STATUS_FALSE } from '../constants';
+import fluxKustomizationStatusQuery from '../graphql/queries/flux_kustomization_status.query.graphql';
+import fluxHelmReleaseStatusQuery from '../graphql/queries/flux_helm_release_status.query.graphql';
export default {
components: {
@@ -17,23 +19,137 @@ export default {
return ['error', 'success', ''].includes(val);
},
},
+ configuration: {
+ required: true,
+ type: Object,
+ },
+ environmentName: {
+ required: true,
+ type: String,
+ },
+ namespace: {
+ required: false,
+ type: String,
+ default: '',
+ },
+ },
+ apollo: {
+ fluxKustomizationStatus: {
+ query: fluxKustomizationStatusQuery,
+ variables() {
+ return {
+ configuration: this.configuration,
+ namespace: this.namespace,
+ environmentName: this.environmentName.toLowerCase(),
+ };
+ },
+ skip() {
+ return !this.namespace;
+ },
+ },
+ fluxHelmReleaseStatus: {
+ query: fluxHelmReleaseStatusQuery,
+ variables() {
+ return {
+ configuration: this.configuration,
+ namespace: this.namespace,
+ environmentName: this.environmentName.toLowerCase(),
+ };
+ },
+ skip() {
+ return Boolean(
+ !this.namespace ||
+ this.$apollo.queries.fluxKustomizationStatus.loading ||
+ this.hasKustomizations,
+ );
+ },
+ },
},
computed: {
healthBadge() {
return HEALTH_BADGES[this.clusterHealthStatus];
},
+ hasKustomizations() {
+ return this.fluxKustomizationStatus?.length;
+ },
+ hasHelmReleases() {
+ return this.fluxHelmReleaseStatus?.length;
+ },
+ isLoading() {
+ return (
+ this.$apollo.queries.fluxKustomizationStatus.loading ||
+ this.$apollo.queries.fluxHelmReleaseStatus.loading
+ );
+ },
+ fluxCRD() {
+ if (!this.hasKustomizations && !this.hasHelmReleases) {
+ return [];
+ }
+
+ return this.hasKustomizations ? this.fluxKustomizationStatus : this.fluxHelmReleaseStatus;
+ },
+ fluxAnyStalled() {
+ return this.fluxCRD.find((condition) => {
+ return condition.status === STATUS_TRUE && condition.type === 'Stalled';
+ });
+ },
+ fluxAnyReconciling() {
+ return this.fluxCRD.find((condition) => {
+ return condition.status === STATUS_TRUE && condition.type === 'Reconciling';
+ });
+ },
+ fluxAnyReconciled() {
+ return this.fluxCRD.find((condition) => {
+ return condition.status === STATUS_TRUE && condition.type === 'Ready';
+ });
+ },
+ fluxAnyFailed() {
+ return this.fluxCRD.find((condition) => {
+ return condition.status === STATUS_FALSE && condition.type === 'Ready';
+ });
+ },
+ syncStatusBadge() {
+ if (!this.fluxCRD.length) {
+ return SYNC_STATUS_BADGES.unavailable;
+ } else if (this.fluxAnyFailed) {
+ return SYNC_STATUS_BADGES.failed;
+ } else if (this.fluxAnyStalled) {
+ return SYNC_STATUS_BADGES.stalled;
+ } else if (this.fluxAnyReconciling) {
+ return SYNC_STATUS_BADGES.reconciling;
+ } else if (this.fluxAnyReconciled) {
+ return SYNC_STATUS_BADGES.reconciled;
+ }
+ return SYNC_STATUS_BADGES.unknown;
+ },
},
i18n: {
healthLabel: s__('Environment|Environment health'),
+ syncStatusLabel: s__('Environment|Sync status'),
},
+ badgeContainerClasses: 'gl-display-flex gl-align-items-center gl-flex-shrink-0 gl-mr-3 gl-mb-2',
};
</script>
<template>
- <div class="gl-display-flex gl-align-items-center gl-mr-3 gl-mb-2">
- <span class="gl-font-sm gl-font-monospace gl-mr-3">{{ $options.i18n.healthLabel }}</span>
- <gl-loading-icon v-if="!clusterHealthStatus" size="sm" inline />
- <gl-badge v-else-if="healthBadge" :variant="healthBadge.variant">
- {{ healthBadge.text }}
- </gl-badge>
+ <div class="gl-display-flex gl-flex-wrap">
+ <div :class="$options.badgeContainerClasses">
+ <span class="gl-mr-3">{{ $options.i18n.healthLabel }}</span>
+ <gl-loading-icon v-if="!clusterHealthStatus" size="sm" inline />
+ <gl-badge v-else-if="healthBadge" :variant="healthBadge.variant" data-testid="health-badge">
+ {{ healthBadge.text }}
+ </gl-badge>
+ </div>
+
+ <div :class="$options.badgeContainerClasses">
+ <span class="gl-mr-3">{{ $options.i18n.syncStatusLabel }}</span>
+ <gl-loading-icon v-if="isLoading" size="sm" inline />
+ <gl-badge
+ v-else-if="syncStatusBadge"
+ :icon="syncStatusBadge.icon"
+ :variant="syncStatusBadge.variant"
+ data-testid="sync-badge"
+ >{{ syncStatusBadge.text }}</gl-badge
+ >
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue
index fda1c85f739..555d525c3b6 100644
--- a/app/assets/javascripts/environments/components/new_environment_item.vue
+++ b/app/assets/javascripts/environments/components/new_environment_item.vue
@@ -372,7 +372,11 @@ export default {
</gl-sprintf>
</div>
<div v-if="clusterAgent" :class="$options.kubernetesOverviewClasses">
- <kubernetes-overview :cluster-agent="clusterAgent" :namespace="kubernetesNamespace" />
+ <kubernetes-overview
+ :cluster-agent="clusterAgent"
+ :namespace="kubernetesNamespace"
+ :environment-name="environment.name"
+ />
</div>
<div v-if="rolloutStatus" :class="$options.deployBoardClasses">
<deploy-board-wrapper
diff --git a/app/assets/javascripts/environments/constants.js b/app/assets/javascripts/environments/constants.js
index dc9481a5429..921be902bdc 100644
--- a/app/assets/javascripts/environments/constants.js
+++ b/app/assets/javascripts/environments/constants.js
@@ -104,6 +104,42 @@ export const HEALTH_BADGES = {
},
};
+export const SYNC_STATUS_BADGES = {
+ reconciled: {
+ variant: 'success',
+ icon: 'status_success',
+ text: s__('Environment|Reconciled'),
+ },
+ reconciling: {
+ variant: 'info',
+ icon: 'status_running',
+ text: s__('Environment|Reconciling'),
+ },
+ stalled: {
+ variant: 'warning',
+ icon: 'status_pending',
+ text: s__('Environment|Stalled'),
+ },
+ failed: {
+ variant: 'danger',
+ icon: 'status_failed',
+ text: s__('Deployment|Failed'),
+ },
+ unknown: {
+ variant: 'neutral',
+ icon: 'status_notfound',
+ text: s__('Deployment|Unknown'),
+ },
+ unavailable: {
+ variant: 'muted',
+ icon: 'status_notfound',
+ text: s__('Deployment|Unavailable'),
+ },
+};
+
+export const STATUS_TRUE = 'True';
+export const STATUS_FALSE = 'False';
+
export const PHASE_RUNNING = 'Running';
export const PHASE_PENDING = 'Pending';
export const PHASE_SUCCEEDED = 'Succeeded';
diff --git a/app/assets/javascripts/environments/graphql/client.js b/app/assets/javascripts/environments/graphql/client.js
index 553b06e632f..8faed710402 100644
--- a/app/assets/javascripts/environments/graphql/client.js
+++ b/app/assets/javascripts/environments/graphql/client.js
@@ -9,6 +9,8 @@ import k8sPodsQuery from './queries/k8s_pods.query.graphql';
import k8sServicesQuery from './queries/k8s_services.query.graphql';
import k8sWorkloadsQuery from './queries/k8s_workloads.query.graphql';
import k8sNamespacesQuery from './queries/k8s_namespaces.query.graphql';
+import fluxKustomizationStatusQuery from './queries/flux_kustomization_status.query.graphql';
+import fluxHelmReleaseStatusQuery from './queries/flux_helm_release_status.query.graphql';
import { resolvers } from './resolvers';
import typeDefs from './typedefs.graphql';
@@ -170,6 +172,21 @@ export const apolloProvider = (endpoint) => {
},
},
});
+ cache.writeQuery({
+ query: fluxKustomizationStatusQuery,
+ data: {
+ status: '',
+ type: '',
+ },
+ });
+ cache.writeQuery({
+ query: fluxHelmReleaseStatusQuery,
+ data: {
+ status: '',
+ type: '',
+ },
+ });
+
return new VueApollo({
defaultClient,
});
diff --git a/app/assets/javascripts/environments/graphql/queries/flux_helm_release_status.query.graphql b/app/assets/javascripts/environments/graphql/queries/flux_helm_release_status.query.graphql
new file mode 100644
index 00000000000..0857f6f57df
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/queries/flux_helm_release_status.query.graphql
@@ -0,0 +1,14 @@
+query getFluxHelmReleaseStatusQuery(
+ $configuration: LocalConfiguration
+ $namespace: String
+ $environmentName: String
+) {
+ fluxHelmReleaseStatus(
+ configuration: $configuration
+ namespace: $namespace
+ environmentName: $environmentName
+ ) @client {
+ status
+ type
+ }
+}
diff --git a/app/assets/javascripts/environments/graphql/queries/flux_kustomization_status.query.graphql b/app/assets/javascripts/environments/graphql/queries/flux_kustomization_status.query.graphql
new file mode 100644
index 00000000000..4eaea014718
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/queries/flux_kustomization_status.query.graphql
@@ -0,0 +1,14 @@
+query getFluxHelmKustomizationStatusQuery(
+ $configuration: LocalConfiguration
+ $namespace: String
+ $environmentName: String
+) {
+ fluxKustomizationStatus(
+ configuration: $configuration
+ namespace: $namespace
+ environmentName: $environmentName
+ ) @client {
+ status
+ type
+ }
+}
diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js
index 8cfe44c5a05..fcc17cea4e1 100644
--- a/app/assets/javascripts/environments/graphql/resolvers.js
+++ b/app/assets/javascripts/environments/graphql/resolvers.js
@@ -16,6 +16,11 @@ import environmentToChangeCanaryQuery from './queries/environment_to_change_cana
import isEnvironmentStoppingQuery from './queries/is_environment_stopping.query.graphql';
import pageInfoQuery from './queries/page_info.query.graphql';
+const helmReleasesResourceType = 'helmreleases';
+const kustomizationsResourceType = 'kustomizations';
+const helmReleasesApiVersion = 'helm.toolkit.fluxcd.io/v2beta1';
+const kustomizationsApiVersion = 'kustomize.toolkit.fluxcd.io/v1beta1';
+
const buildErrors = (errors = []) => ({
errors,
__typename: 'LocalEnvironmentErrors',
@@ -78,6 +83,30 @@ const handleClusterError = (err) => {
throw error;
};
+const buildFluxResourceUrl = ({
+ basePath,
+ namespace,
+ apiVersion,
+ resourceType,
+ environmentName,
+}) => {
+ return `${basePath}/apis/${apiVersion}/namespaces/${namespace}/${resourceType}/${environmentName}`;
+};
+
+const getFluxResourceStatus = (configuration, url) => {
+ const { headers } = configuration.baseOptions;
+ const withCredentials = true;
+
+ return axios
+ .get(url, { withCredentials, headers })
+ .then((res) => {
+ return res?.data?.status?.conditions || [];
+ })
+ .catch((err) => {
+ handleClusterError(err);
+ });
+};
+
export const resolvers = (endpoint) => ({
Query: {
environmentApp(_context, { page, scope, search }, { cache }) {
@@ -223,6 +252,26 @@ export const resolvers = (endpoint) => ({
throw new Error(humanizeClusterErrors(error));
});
},
+ fluxKustomizationStatus(_, { configuration, namespace, environmentName }) {
+ const url = buildFluxResourceUrl({
+ basePath: configuration.basePath,
+ resourceType: kustomizationsResourceType,
+ apiVersion: kustomizationsApiVersion,
+ namespace,
+ environmentName,
+ });
+ return getFluxResourceStatus(configuration, url);
+ },
+ fluxHelmReleaseStatus(_, { configuration, namespace, environmentName }) {
+ const url = buildFluxResourceUrl({
+ basePath: configuration.basePath,
+ resourceType: helmReleasesResourceType,
+ apiVersion: helmReleasesApiVersion,
+ namespace,
+ environmentName,
+ });
+ return getFluxResourceStatus(configuration, url);
+ },
},
Mutation: {
stopEnvironmentREST(_, { environment }, { client }) {
diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql
index e2c22dda554..41f165ad1da 100644
--- a/app/assets/javascripts/environments/graphql/typedefs.graphql
+++ b/app/assets/javascripts/environments/graphql/typedefs.graphql
@@ -167,6 +167,11 @@ type LocalK8sNamespaces {
metadata: k8sNamespaceMetadata
}
+type LocalFluxResourceStatus {
+ status: String
+ type: String
+}
+
extend type Query {
environmentApp(page: Int, scope: String): LocalEnvironmentApp
folder(environment: NestedLocalEnvironmentInput): LocalEnvironmentFolder
@@ -179,6 +184,16 @@ extend type Query {
k8sPods(configuration: LocalConfiguration, namespace: String): [LocalK8sPods]
k8sServices(configuration: LocalConfiguration): [LocalK8sServices]
k8sWorkloads(configuration: LocalConfiguration, namespace: String): LocalK8sWorkloads
+ fluxKustomizationStatus(
+ configuration: LocalConfiguration
+ namespace: String
+ environmentName: String
+ ): LocalFluxResourceStatus
+ fluxHelmReleaseStatus(
+ configuration: LocalConfiguration
+ namespace: String
+ environmentName: String
+ ): LocalFluxResourceStatus
}
extend type Mutation {
diff --git a/app/assets/javascripts/group_settings/components/shared_runners_form.vue b/app/assets/javascripts/group_settings/components/shared_runners_form.vue
index a4ec48ffd2f..d396169c0a3 100644
--- a/app/assets/javascripts/group_settings/components/shared_runners_form.vue
+++ b/app/assets/javascripts/group_settings/components/shared_runners_form.vue
@@ -1,5 +1,5 @@
<script>
-import { GlToggle, GlAlert } from '@gitlab/ui';
+import { GlAlert, GlLink, GlSprintf, GlToggle } from '@gitlab/ui';
import { sprintf } from '~/locale';
import { updateGroup } from '~/api/groups_api';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
@@ -14,19 +14,29 @@ import {
export default {
components: {
- GlToggle,
GlAlert,
+ GlLink,
+ GlSprintf,
+ GlToggle,
+ },
+ inject: {
+ groupId: {},
+ groupName: {},
+ groupIsEmpty: {},
+ sharedRunnersSetting: {},
+
+ runnerEnabledValue: {},
+ runnerDisabledValue: {},
+ runnerAllowOverrideValue: {},
+
+ // Parent group, only present in sub-groups
+
+ parentSharedRunnersSetting: { default: null },
+
+ // Available when user can admin parent
+ parentName: { default: null },
+ parentSettingsPath: { default: null },
},
- inject: [
- 'groupId',
- 'groupName',
- 'groupIsEmpty',
- 'sharedRunnersSetting',
- 'parentSharedRunnersSetting',
- 'runnerEnabledValue',
- 'runnerDisabledValue',
- 'runnerAllowOverrideValue',
- ],
data() {
return {
isLoading: false,
@@ -48,6 +58,9 @@ export default {
overrideToggleValue() {
return this.value === this.runnerAllowOverrideValue;
},
+ isParentAvailable() {
+ return this.parentSettingsPath && this.parentName;
+ },
},
methods: {
async onSharedRunnersToggle(enabled) {
@@ -109,26 +122,28 @@ export default {
<gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mb-5">
{{ error }}
</gl-alert>
-
- <gl-alert
- v-if="isSharedRunnersToggleDisabled"
- variant="warning"
- :dismissible="false"
- class="gl-mb-5"
- >
- {{ __('Shared runners are disabled for the parent group') }}
- </gl-alert>
-
<section class="gl-mb-5">
<gl-toggle
:value="sharedRunnersToggleValue"
:is-loading="isLoading"
:disabled="isSharedRunnersToggleDisabled"
:label="__('Enable shared runners for this group')"
- :help="__('Enable shared runners for all projects and subgroups in this group.')"
+ :description="__('Enable shared runners for all projects and subgroups in this group.')"
data-testid="shared-runners-toggle"
@change="onSharedRunnersToggle"
- />
+ >
+ <template v-if="isSharedRunnersToggleDisabled" #help>
+ {{ s__('Runners|Shared runners are disabled.') }}
+ <gl-sprintf
+ v-if="isParentAvailable"
+ :message="s__('Runners|Go to %{groupLink} to enable them.')"
+ >
+ <template #groupLink>
+ <gl-link :href="parentSettingsPath">{{ parentName }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ </gl-toggle>
</section>
<section class="gl-mb-5">
@@ -137,10 +152,24 @@ export default {
:is-loading="isLoading"
:disabled="isOverrideToggleDisabled"
:label="__('Allow projects and subgroups to override the group setting')"
- :help="__('Allows projects or subgroups in this group to override the global setting.')"
+ :description="
+ __('Allows projects or subgroups in this group to override the global setting.')
+ "
data-testid="override-runners-toggle"
@change="onOverrideToggle"
- />
+ >
+ <template v-if="isSharedRunnersToggleDisabled" #help>
+ {{ s__('Runners|Shared runners are disabled.') }}
+ <gl-sprintf
+ v-if="isParentAvailable"
+ :message="s__('Runners|Go to %{groupLink} to enable them.')"
+ >
+ <template #groupLink>
+ <gl-link :href="parentSettingsPath">{{ parentName }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ </gl-toggle>
</section>
</div>
</template>
diff --git a/app/assets/javascripts/group_settings/mount_shared_runners.js b/app/assets/javascripts/group_settings/mount_shared_runners.js
index 0767330cd54..334192a6f87 100644
--- a/app/assets/javascripts/group_settings/mount_shared_runners.js
+++ b/app/assets/javascripts/group_settings/mount_shared_runners.js
@@ -10,6 +10,8 @@ export default (containerId = 'update-shared-runners-form') => {
groupName,
groupIsEmpty,
sharedRunnersSetting,
+ parentName,
+ parentSettingsPath,
parentSharedRunnersSetting,
runnerEnabledValue,
runnerDisabledValue,
@@ -23,10 +25,14 @@ export default (containerId = 'update-shared-runners-form') => {
groupName,
groupIsEmpty: parseBoolean(groupIsEmpty),
sharedRunnersSetting,
- parentSharedRunnersSetting,
+
runnerEnabledValue,
runnerDisabledValue,
runnerAllowOverrideValue,
+
+ parentName,
+ parentSettingsPath,
+ parentSharedRunnersSetting,
},
render(createElement) {
return createElement(UpdateSharedRunnersForm);
diff --git a/app/assets/javascripts/token_access/components/inbound_token_access.vue b/app/assets/javascripts/token_access/components/inbound_token_access.vue
index ac359b4f901..234ac0505b2 100644
--- a/app/assets/javascripts/token_access/components/inbound_token_access.vue
+++ b/app/assets/javascripts/token_access/components/inbound_token_access.vue
@@ -5,6 +5,7 @@ import {
GlCard,
GlFormInput,
GlLink,
+ GlIcon,
GlLoadingIcon,
GlSprintf,
GlToggle,
@@ -64,6 +65,7 @@ export default {
GlCard,
GlFormInput,
GlLink,
+ GlIcon,
GlLoadingIcon,
GlSprintf,
GlToggle,
@@ -109,6 +111,7 @@ export default {
inboundJobTokenScopeEnabled: null,
targetProjectPath: '',
projects: [],
+ isAddFormVisible: false,
};
},
computed: {
@@ -193,10 +196,14 @@ export default {
},
clearTargetProjectPath() {
this.targetProjectPath = '';
+ this.isAddFormVisible = false;
},
getProjects() {
this.$apollo.queries.projects.refetch();
},
+ showAddForm() {
+ this.isAddFormVisible = true;
+ },
},
};
</script>
@@ -228,22 +235,55 @@ export default {
</gl-toggle>
<div>
- <gl-card class="gl-mt-5 gl-mb-3">
+ <gl-card
+ class="gl-new-card"
+ header-class="gl-new-card-header"
+ body-class="gl-new-card-body gl-px-0"
+ >
<template #header>
- <h5 class="gl-my-0">{{ $options.i18n.cardHeaderTitle }}</h5>
+ <div class="gl-new-card-title-wrapper">
+ <h5 class="gl-new-card-title">{{ $options.i18n.cardHeaderTitle }}</h5>
+ <span class="gl-new-card-count">
+ <gl-icon name="project" class="gl-mr-2" />
+ {{ projects.length }}
+ </span>
+ </div>
+ <div class="gl-new-card-actions">
+ <gl-button
+ v-if="!isAddFormVisible"
+ size="small"
+ data-testid="toggle-form-btn"
+ @click="showAddForm"
+ >{{ $options.i18n.addProject }}</gl-button
+ >
+ </div>
</template>
- <template #default>
+
+ <div v-if="isAddFormVisible" class="gl-new-card-add-form gl-m-3">
+ <h4 class="gl-mt-0">{{ $options.i18n.addProject }}</h4>
<gl-form-input
v-model="targetProjectPath"
:placeholder="$options.i18n.addProjectPlaceholder"
/>
- </template>
- <template #footer>
- <gl-button variant="confirm" :disabled="isProjectPathEmpty" @click="addProject">
- {{ $options.i18n.addProject }}
- </gl-button>
- <gl-button @click="clearTargetProjectPath">{{ $options.i18n.cancel }}</gl-button>
- </template>
+ <div class="gl-display-flex gl-mt-5">
+ <gl-button
+ variant="confirm"
+ :disabled="isProjectPathEmpty"
+ class="gl-mr-3"
+ data-testid="add-project-btn"
+ @click="addProject"
+ >
+ {{ $options.i18n.addProject }}
+ </gl-button>
+ <gl-button @click="clearTargetProjectPath">{{ $options.i18n.cancel }}</gl-button>
+ </div>
+ </div>
+
+ <token-projects-table
+ :projects="projects"
+ :table-fields="$options.fields"
+ @removeProject="removeProject"
+ />
</gl-card>
<gl-alert
v-if="!inboundJobTokenScopeEnabled"
@@ -254,11 +294,6 @@ export default {
>
{{ $options.i18n.settingDisabledMessage }}
</gl-alert>
- <token-projects-table
- :projects="projects"
- :table-fields="$options.fields"
- @removeProject="removeProject"
- />
</div>
</template>
</div>
diff --git a/app/assets/javascripts/token_access/components/outbound_token_access.vue b/app/assets/javascripts/token_access/components/outbound_token_access.vue
index cad1fff062a..7e1e6cc445c 100644
--- a/app/assets/javascripts/token_access/components/outbound_token_access.vue
+++ b/app/assets/javascripts/token_access/components/outbound_token_access.vue
@@ -3,8 +3,8 @@ import {
GlAlert,
GlButton,
GlCard,
- GlFormInput,
GlLink,
+ GlIcon,
GlLoadingIcon,
GlSprintf,
GlToggle,
@@ -71,8 +71,8 @@ export default {
GlAlert,
GlButton,
GlCard,
- GlFormInput,
GlLink,
+ GlIcon,
GlLoadingIcon,
GlSprintf,
GlToggle,
@@ -221,7 +221,7 @@ export default {
<gl-loading-icon v-if="$apollo.loading" size="lg" class="gl-mt-5" />
<template v-else>
<gl-alert
- class="gl-mb-3"
+ class="gl-mt-5 gl-mb-3"
variant="warning"
:dismissible="false"
:show-icon="false"
@@ -268,30 +268,29 @@ export default {
</gl-toggle>
<div>
- <gl-card class="gl-mt-5 gl-mb-3">
+ <gl-card
+ class="gl-new-card"
+ header-class="gl-new-card-header"
+ body-class="gl-new-card-body gl-px-0"
+ >
<template #header>
- <h5 class="gl-my-0">{{ $options.i18n.cardHeaderTitle }}</h5>
- </template>
- <template #default>
- <gl-form-input
- v-model="targetProjectPath"
- :disabled="true"
- :placeholder="$options.i18n.addProjectPlaceholder"
- data-testid="project-path-input"
- />
- </template>
- <template #footer>
- <gl-button variant="confirm" :disabled="isProjectPathEmpty" @click="addProject">
- {{ $options.i18n.addProject }}
- </gl-button>
- <gl-button @click="clearTargetProjectPath">{{ $options.i18n.cancel }}</gl-button>
+ <div class="gl-new-card-title-wrapper">
+ <h5 class="gl-new-card-title">{{ $options.i18n.cardHeaderTitle }}</h5>
+ <span class="gl-new-card-count">
+ <gl-icon name="project" class="gl-mr-2" />
+ {{ projects.length }}
+ </span>
+ </div>
+ <div class="gl-new-card-actions">
+ <gl-button size="small" disabled>{{ $options.i18n.addProject }}</gl-button>
+ </div>
</template>
+ <token-projects-table
+ :projects="projects"
+ :table-fields="$options.fields"
+ @removeProject="removeProject"
+ />
</gl-card>
- <token-projects-table
- :projects="projects"
- :table-fields="$options.fields"
- @removeProject="removeProject"
- />
</div>
</template>
</div>
diff --git a/app/assets/javascripts/token_access/components/token_access_app.vue b/app/assets/javascripts/token_access/components/token_access_app.vue
index 167eebc8d9b..d2d5e6b2a5a 100644
--- a/app/assets/javascripts/token_access/components/token_access_app.vue
+++ b/app/assets/javascripts/token_access/components/token_access_app.vue
@@ -12,7 +12,7 @@ export default {
</script>
<template>
<div>
- <inbound-token-access class="gl-pb-5" />
- <outbound-token-access class="gl-py-5" />
+ <inbound-token-access />
+ <outbound-token-access />
</div>
</template>