Commit 186b06fc authored by Alexander Turinske's avatar Alexander Turinske Committed by Terri Chu

Add `cluster_vulnerabilities` feature flag

parent f77caca4
......@@ -128,6 +128,7 @@ export default {
</p>
<gl-tabs>
<slot name="ee-security-tab"></slot>
<gl-tab>
<template #title>
<span data-testid="cluster-agent-token-count">
......
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import AgentShowPage from './components/show.vue';
import AgentShowPage from 'ee_else_ce/clusters/agents/components/show.vue';
Vue.use(VueApollo);
......
......@@ -3,6 +3,10 @@
class Projects::ClusterAgentsController < Projects::ApplicationController
before_action :authorize_can_read_cluster_agent!
before_action do
push_frontend_feature_flag(:cluster_vulnerabilities, project, default_enabled: :yaml)
end
feature_category :kubernetes_management
def show
......@@ -17,3 +21,5 @@ class Projects::ClusterAgentsController < Projects::ApplicationController
access_denied!
end
end
Projects::ClusterAgentsController.prepend_mod_with('Projects::ClusterAgentsController')
---
name: cluster_vulnerabilities
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73321
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343917
milestone: '14.5'
type: development
group: group::container security
default_enabled: false
<script>
import { GlTab } from '@gitlab/ui';
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AgentShowPage from '~/clusters/agents/components/show.vue';
export default {
i18n: {
securityTabTitle: s__('ClusterAgents|Security'),
},
components: { AgentShowPage, GlTab },
mixins: [glFeatureFlagMixin()],
props: {
agentName: {
required: true,
type: String,
},
projectPath: {
required: true,
type: String,
},
},
computed: {
showSecurityTab() {
return (
this.glFeatures.kubernetesClusterVulnerabilities && this.glFeatures.clusterVulnerabilities
);
},
},
};
</script>
<template>
<agent-show-page v-bind="$props">
<template v-if="showSecurityTab" #ee-security-tab>
<!-- Placeholder for https://gitlab.com/gitlab-org/gitlab/-/issues/343912-->
<gl-tab :title="$options.i18n.securityTabTitle"><div></div></gl-tab>
</template>
</agent-show-page>
</template>
# frozen_string_literal: true
module EE
module Projects
module ClusterAgentsController
extend ActiveSupport::Concern
prepended do
before_action do
push_licensed_feature(:kubernetes_cluster_vulnerabilities, project)
end
end
end
end
end
......@@ -174,6 +174,7 @@ class License < ApplicationRecord
issuable_health_status
jira_vulnerabilities_integration
jira_issue_association_enforcement
kubernetes_cluster_vulnerabilities
license_scanning
personal_access_token_expiration_policy
prometheus_alerts
......
import { GlTab } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ClusterAgentShow from 'ee/clusters/agents/components/show.vue';
import CEClusterAgentShowPage from '~/clusters/agents/components/show.vue';
describe('ClusterAgentShow', () => {
let wrapper;
const agentName = 'best-agent';
const projectPath = 'path/to/project';
const createWrapper = ({ glFeatures = {} } = {}) => {
wrapper = extendedWrapper(
shallowMount(ClusterAgentShow, {
propsData: { agentName, projectPath },
provide: { glFeatures },
}),
);
};
const findTab = () => wrapper.findComponent(GlTab);
const findCEClusterAgentShowPage = () => wrapper.findComponent(CEClusterAgentShowPage);
afterEach(() => {
wrapper.destroy();
});
describe('tab behavior', () => {
it.each`
title | glFeatures | tabStatus
${'does not display the tab when no glFeatures are available'} | ${{}} | ${false}
${'does not display the tab when only the "clusterVulnerabilities" flag is true'} | ${{ clusterVulnerabilities: true }} | ${false}
${'does not display the tab when only the "kubernetesClusterVulnerabilities" flag is true'} | ${{ kubernetesClusterVulnerabilities: true }} | ${false}
${'does display the tab when both the "kubernetesClusterVulnerabilities" flag and "clusterVulnerabilities" flag are true'} | ${{ clusterVulnerabilities: true, kubernetesClusterVulnerabilities: true }} | ${true}
`('$title', async ({ glFeatures, tabStatus }) => {
createWrapper({ glFeatures });
await nextTick();
expect(findCEClusterAgentShowPage().props()).toStrictEqual({ agentName, projectPath });
expect(findTab().exists()).toBe(tabStatus);
});
});
});
......@@ -7403,6 +7403,9 @@ msgstr ""
msgid "ClusterAgents|Registration token"
msgstr ""
msgid "ClusterAgents|Security"
msgstr ""
msgid "ClusterAgents|Select an Agent"
msgstr ""
......
import { GlAlert, GlKeysetPagination, GlLoadingIcon, GlSprintf, GlTab } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ClusterAgentShow from '~/clusters/agents/components/show.vue';
import TokenTable from '~/clusters/agents/components/token_table.vue';
import getAgentQuery from '~/clusters/agents/graphql/queries/get_cluster_agent.query.graphql';
......@@ -40,28 +42,34 @@ describe('ClusterAgentShow', () => {
queryResponse || jest.fn().mockResolvedValue({ data: { project: { clusterAgent } } });
const apolloProvider = createMockApollo([[getAgentQuery, agentQueryResponse]]);
wrapper = shallowMount(ClusterAgentShow, {
wrapper = extendedWrapper(
shallowMount(ClusterAgentShow, {
localVue,
apolloProvider,
propsData,
stubs: { GlSprintf, TimeAgoTooltip, GlTab },
});
}),
);
};
const createWrapperWithoutApollo = ({ clusterAgent, loading = false }) => {
const createWrapperWithoutApollo = ({ clusterAgent, loading = false, slots = {} }) => {
const $apollo = { queries: { clusterAgent: { loading } } };
wrapper = shallowMount(ClusterAgentShow, {
wrapper = extendedWrapper(
shallowMount(ClusterAgentShow, {
propsData,
mocks: { $apollo, clusterAgent },
slots,
stubs: { GlTab },
});
}),
);
};
const findCreatedText = () => wrapper.find('[data-testid="cluster-agent-create-info"]').text();
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findPaginationButtons = () => wrapper.find(GlKeysetPagination);
const findTokenCount = () => wrapper.find('[data-testid="cluster-agent-token-count"]').text();
const findCreatedText = () => wrapper.findByTestId('cluster-agent-create-info').text();
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findPaginationButtons = () => wrapper.findComponent(GlKeysetPagination);
const findTokenCount = () => wrapper.findByTestId('cluster-agent-token-count').text();
const findEESecurityTabSlot = () => wrapper.findByTestId('ee-security-tab');
afterEach(() => {
wrapper.destroy();
......@@ -87,7 +95,7 @@ describe('ClusterAgentShow', () => {
});
it('renders token table', () => {
expect(wrapper.find(TokenTable).exists()).toBe(true);
expect(wrapper.findComponent(TokenTable).exists()).toBe(true);
});
it('should not render pagination buttons when there are no additional pages', () => {
......@@ -188,8 +196,27 @@ describe('ClusterAgentShow', () => {
});
it('displays an alert message', () => {
expect(wrapper.find(GlAlert).exists()).toBe(true);
expect(wrapper.findComponent(GlAlert).exists()).toBe(true);
expect(wrapper.text()).toContain(ClusterAgentShow.i18n.loadingError);
});
});
describe('ee-security-tab slot', () => {
it('does not display when a slot is not passed in', async () => {
createWrapperWithoutApollo({ clusterAgent: defaultClusterAgent });
await nextTick();
expect(findEESecurityTabSlot().exists()).toBe(false);
});
it('does display when a slot is passed in', async () => {
createWrapperWithoutApollo({
clusterAgent: defaultClusterAgent,
slots: {
'ee-security-tab': `<gl-tab data-testid="ee-security-tab">Security Tab!</gl-tab>`,
},
});
await nextTick();
expect(findEESecurityTabSlot().exists()).toBe(true);
});
});
});
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