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-04-25 21:08:55 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-25 21:08:55 +0300
commit1e3f5ab634699e9d50779f05d2ae8dfc8a3ab9b3 (patch)
treec96727e136f4dc4fdbc1190895439d41e0b07fc5 /app/assets/javascripts/environments
parentba8e92f7c9938d7dba333d2396cdd14bfa0de726 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/environments')
-rw-r--r--app/assets/javascripts/environments/components/kubernetes_overview.vue6
-rw-r--r--app/assets/javascripts/environments/components/kubernetes_tabs.vue158
-rw-r--r--app/assets/javascripts/environments/constants.js2
-rw-r--r--app/assets/javascripts/environments/graphql/client.js19
-rw-r--r--app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql2
-rw-r--r--app/assets/javascripts/environments/graphql/queries/k8s_services.query.graphql15
-rw-r--r--app/assets/javascripts/environments/graphql/resolvers.js24
-rw-r--r--app/assets/javascripts/environments/graphql/typedefs.graphql19
-rw-r--r--app/assets/javascripts/environments/helpers/k8s_integration_helper.js36
9 files changed, 279 insertions, 2 deletions
diff --git a/app/assets/javascripts/environments/components/kubernetes_overview.vue b/app/assets/javascripts/environments/components/kubernetes_overview.vue
index 736eaa7062d..41abfcf6dc8 100644
--- a/app/assets/javascripts/environments/components/kubernetes_overview.vue
+++ b/app/assets/javascripts/environments/components/kubernetes_overview.vue
@@ -5,6 +5,7 @@ import csrf from '~/lib/utils/csrf';
import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
import KubernetesAgentInfo from './kubernetes_agent_info.vue';
import KubernetesPods from './kubernetes_pods.vue';
+import KubernetesTabs from './kubernetes_tabs.vue';
export default {
components: {
@@ -13,6 +14,7 @@ export default {
GlAlert,
KubernetesAgentInfo,
KubernetesPods,
+ KubernetesTabs,
},
inject: ['kasTunnelUrl'],
props: {
@@ -103,6 +105,10 @@ export default {
:configuration="k8sAccessConfiguration"
:namespace="namespace"
class="gl-mb-5"
+ @cluster-error="onClusterError" />
+ <kubernetes-tabs
+ :configuration="k8sAccessConfiguration"
+ class="gl-mb-5"
@cluster-error="onClusterError"
/></template>
</gl-collapse>
diff --git a/app/assets/javascripts/environments/components/kubernetes_tabs.vue b/app/assets/javascripts/environments/components/kubernetes_tabs.vue
new file mode 100644
index 00000000000..b1eb92a4049
--- /dev/null
+++ b/app/assets/javascripts/environments/components/kubernetes_tabs.vue
@@ -0,0 +1,158 @@
+<script>
+import { GlTabs, GlTab, GlLoadingIcon, GlBadge, GlTable, GlPagination } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
+import k8sServicesQuery from '../graphql/queries/k8s_services.query.graphql';
+import { generateServicePortsString, getServiceAge } from '../helpers/k8s_integration_helper';
+import { SERVICES_LIMIT_PER_PAGE } from '../constants';
+
+const tableHeadingClasses = 'gl-bg-gray-50! gl-font-weight-bold gl-white-space-nowrap';
+
+export default {
+ components: {
+ GlTabs,
+ GlTab,
+ GlBadge,
+ GlTable,
+ GlPagination,
+ GlLoadingIcon,
+ },
+ apollo: {
+ k8sServices: {
+ query: k8sServicesQuery,
+ variables() {
+ return {
+ configuration: this.configuration,
+ };
+ },
+ update(data) {
+ return data?.k8sServices || [];
+ },
+ error(error) {
+ this.$emit('cluster-error', error);
+ },
+ },
+ },
+ props: {
+ configuration: {
+ required: true,
+ type: Object,
+ },
+ },
+ data() {
+ return {
+ currentPage: 1,
+ };
+ },
+ computed: {
+ servicesItems() {
+ if (!this.k8sServices?.length) return [];
+
+ return this.k8sServices.map((service) => {
+ return {
+ name: service?.metadata?.name,
+ namespace: service?.metadata?.namespace,
+ type: service?.spec?.type,
+ clusterIP: service?.spec?.clusterIP,
+ externalIP: service?.spec?.externalIP,
+ ports: generateServicePortsString(service?.spec?.ports),
+ age: getServiceAge(service?.metadata?.creationTimestamp),
+ };
+ });
+ },
+ servicesLoading() {
+ return this.$apollo.queries.k8sServices.loading;
+ },
+ showPagination() {
+ return this.servicesItems.length > SERVICES_LIMIT_PER_PAGE;
+ },
+ prevPage() {
+ return Math.max(this.currentPage - 1, 0);
+ },
+ nextPage() {
+ const nextPage = this.currentPage + 1;
+ return nextPage > Math.ceil(this.servicesItems.length / SERVICES_LIMIT_PER_PAGE)
+ ? null
+ : nextPage;
+ },
+ },
+ i18n: {
+ servicesTitle: s__('Environment|Services'),
+ name: __('Name'),
+ namespace: __('Namespace'),
+ status: __('Status'),
+ type: __('Type'),
+ clusterIP: s__('Environment|Cluster IP'),
+ externalIP: s__('Environment|External IP'),
+ ports: s__('Environment|Ports'),
+ age: s__('Environment|Age'),
+ },
+ servicesFields: [
+ {
+ key: 'name',
+ label: __('Name'),
+ thClass: tableHeadingClasses,
+ },
+ {
+ key: 'namespace',
+ label: __('Namespace'),
+ thClass: tableHeadingClasses,
+ },
+ {
+ key: 'type',
+ label: __('Type'),
+ thClass: tableHeadingClasses,
+ },
+ {
+ key: 'clusterIP',
+ label: s__('Environment|Cluster IP'),
+ thClass: tableHeadingClasses,
+ },
+ {
+ key: 'externalIP',
+ label: s__('Environment|External IP'),
+ thClass: tableHeadingClasses,
+ },
+ {
+ key: 'ports',
+ label: s__('Environment|Ports'),
+ thClass: tableHeadingClasses,
+ },
+ {
+ key: 'age',
+ label: s__('Environment|Age'),
+ thClass: tableHeadingClasses,
+ },
+ ],
+ SERVICES_LIMIT_PER_PAGE,
+};
+</script>
+<template>
+ <gl-tabs>
+ <gl-tab>
+ <template #title>
+ {{ $options.i18n.servicesTitle }}
+ <gl-badge size="sm" class="gl-tab-counter-badge">{{ servicesItems.length }}</gl-badge>
+ </template>
+
+ <gl-loading-icon v-if="servicesLoading" />
+
+ <gl-table
+ v-else
+ :fields="$options.servicesFields"
+ :items="servicesItems"
+ :per-page="$options.SERVICES_LIMIT_PER_PAGE"
+ :current-page="currentPage"
+ stacked="lg"
+ class="gl-bg-white! gl-mt-3"
+ />
+ <gl-pagination
+ v-if="showPagination"
+ v-model="currentPage"
+ :prev-page="prevPage"
+ :next-page="nextPage"
+ align="center"
+ class="gl-mt-6"
+ />
+ </gl-tab>
+ </gl-tabs>
+</template>
diff --git a/app/assets/javascripts/environments/constants.js b/app/assets/javascripts/environments/constants.js
index 28424322dd2..e675a73ba7d 100644
--- a/app/assets/javascripts/environments/constants.js
+++ b/app/assets/javascripts/environments/constants.js
@@ -87,3 +87,5 @@ export const ENVIRONMENT_NEW_HELP_TEXT = __(
);
export const ENVIRONMENT_EDIT_HELP_TEXT = ENVIRONMENT_NEW_HELP_TEXT;
+
+export const SERVICES_LIMIT_PER_PAGE = 10;
diff --git a/app/assets/javascripts/environments/graphql/client.js b/app/assets/javascripts/environments/graphql/client.js
index 0482741979b..bb6f57e7e80 100644
--- a/app/assets/javascripts/environments/graphql/client.js
+++ b/app/assets/javascripts/environments/graphql/client.js
@@ -6,6 +6,7 @@ import environmentToDeleteQuery from './queries/environment_to_delete.query.grap
import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql';
import environmentToStopQuery from './queries/environment_to_stop.query.graphql';
import k8sPodsQuery from './queries/k8s_pods.query.graphql';
+import k8sServicesQuery from './queries/k8s_services.query.graphql';
import { resolvers } from './resolvers';
import typeDefs from './typedefs.graphql';
@@ -87,7 +88,23 @@ export const apolloProvider = (endpoint) => {
query: k8sPodsQuery,
data: {
status: {
- phase: '',
+ phase: null,
+ },
+ },
+ });
+ cache.writeQuery({
+ query: k8sServicesQuery,
+ data: {
+ metadata: {
+ name: null,
+ namespace: null,
+ creationTimestamp: null,
+ },
+ spec: {
+ type: null,
+ clusterIP: null,
+ externalIP: null,
+ ports: [],
},
},
});
diff --git a/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql b/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql
index 818bca24d51..2d57ede8c15 100644
--- a/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql
+++ b/app/assets/javascripts/environments/graphql/queries/k8s_pods.query.graphql
@@ -1,4 +1,4 @@
-query getK8sPods($configuration: Object, $namespace: String) {
+query getK8sPods($configuration: LocalConfiguration, $namespace: String) {
k8sPods(configuration: $configuration, namespace: $namespace) @client {
status {
phase
diff --git a/app/assets/javascripts/environments/graphql/queries/k8s_services.query.graphql b/app/assets/javascripts/environments/graphql/queries/k8s_services.query.graphql
new file mode 100644
index 00000000000..d97849eecc1
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/queries/k8s_services.query.graphql
@@ -0,0 +1,15 @@
+query getK8sServices($configuration: LocalConfiguration) {
+ k8sServices(configuration: $configuration) @client {
+ metadata {
+ name
+ namespace
+ creationTimestamp
+ }
+ spec {
+ type
+ clusterIP
+ externalIP
+ ports
+ }
+ }
+}
diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js
index 013467e34be..8ebeeb92a53 100644
--- a/app/assets/javascripts/environments/graphql/resolvers.js
+++ b/app/assets/javascripts/environments/graphql/resolvers.js
@@ -85,6 +85,30 @@ export const resolvers = (endpoint) => ({
throw error;
});
},
+ k8sServices(_, { configuration }) {
+ const coreV1Api = new CoreV1Api(new Configuration(configuration));
+ return coreV1Api
+ .listCoreV1ServiceForAllNamespaces()
+ .then((res) => {
+ const items = res?.data?.items || [];
+ return items.map((item) => {
+ const { type, clusterIP, externalIP, ports } = item.spec;
+ return {
+ metadata: item.metadata,
+ spec: {
+ type,
+ clusterIP: clusterIP || '-',
+ externalIP: externalIP || '-',
+ ports,
+ },
+ };
+ });
+ })
+ .catch((err) => {
+ const error = err?.response?.data?.message ? new Error(err.response.data.message) : err;
+ throw error;
+ });
+ },
},
Mutation: {
stopEnvironmentREST(_, { environment }, { client }) {
diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql
index 4de271935d6..b85bf1e10d3 100644
--- a/app/assets/javascripts/environments/graphql/typedefs.graphql
+++ b/app/assets/javascripts/environments/graphql/typedefs.graphql
@@ -75,6 +75,24 @@ input LocalConfiguration {
baseOptions: JSON
}
+type k8sServiceMetadata {
+ name: String
+ namespace: String
+ creationTimestamp: String
+}
+
+type k8sServiceSpec {
+ type: String
+ clusterIP: String
+ externalIP: String
+ ports: JSON
+}
+
+type LocalK8sServices {
+ metadata: k8sServiceMetadata
+ spec: k8sServiceSpec
+}
+
extend type Query {
environmentApp(page: Int, scope: String): LocalEnvironmentApp
folder(environment: NestedLocalEnvironmentInput): LocalEnvironmentFolder
@@ -85,6 +103,7 @@ extend type Query {
isEnvironmentStopping(environment: LocalEnvironmentInput): Boolean
isLastDeployment(environment: LocalEnvironmentInput): Boolean
k8sPods(configuration: LocalConfiguration, namespace: String): [LocalK8sPods]
+ k8sServices(configuration: LocalConfiguration): [LocalK8sServices]
}
extend type Mutation {
diff --git a/app/assets/javascripts/environments/helpers/k8s_integration_helper.js b/app/assets/javascripts/environments/helpers/k8s_integration_helper.js
new file mode 100644
index 00000000000..6a94ac31fa1
--- /dev/null
+++ b/app/assets/javascripts/environments/helpers/k8s_integration_helper.js
@@ -0,0 +1,36 @@
+import { differenceInSeconds } from '~/lib/utils/datetime_utility';
+
+export function generateServicePortsString(ports) {
+ if (!ports?.length) return '';
+
+ return ports
+ .map((port) => {
+ const nodePort = port.nodePort ? `:${port.nodePort}` : '';
+ return `${port.port}${nodePort}/${port.protocol}`;
+ })
+ .join(', ');
+}
+
+export function getServiceAge(creationTimestamp) {
+ if (!creationTimestamp) return '';
+
+ const timeDifference = differenceInSeconds(new Date(creationTimestamp), new Date());
+
+ const seconds = Math.floor(timeDifference);
+ const minutes = Math.floor(seconds / 60) % 60;
+ const hours = Math.floor(seconds / 60 / 60) % 24;
+ const days = Math.floor(seconds / 60 / 60 / 24);
+
+ let ageString;
+ if (days > 0) {
+ ageString = `${days}d`;
+ } else if (hours > 0) {
+ ageString = `${hours}h`;
+ } else if (minutes > 0) {
+ ageString = `${minutes}m`;
+ } else {
+ ageString = `${seconds}s`;
+ }
+
+ return ageString;
+}