Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Secrets Sync] enable access to Sync clients page for HVD clusters #26713

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ui/app/components/clients/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import type {
} from 'core/utils/client-count-utils';

interface Args {
isSecretsSyncActivated?: boolean;
activity: ClientsActivityModel;
versionHistory: ClientsVersionHistoryModel[];
startTimestamp: number;
Expand Down
1 change: 1 addition & 0 deletions ui/app/components/clients/attribution.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { format, isSameMonth } from 'date-fns';
* @param {string} responseTimestamp - ISO timestamp created in serializer to timestamp the response, renders in bottom left corner below attribution chart
* @param {boolean} isHistoricalMonth - when true data is from a single, historical month so side-by-side charts should display for attribution data
* @param {array} upgradesDuringActivity - array of objects containing version history upgrade data
* @param {boolean} isSecretsSyncActivated - boolean to determine if secrets sync is activated
*/

export default class Attribution extends Component {
Expand Down
31 changes: 31 additions & 0 deletions ui/app/components/clients/counts/nav-bar.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}

<nav class="tabs has-bottom-margin-s" aria-label="navigation for managing client counts">
<ul>
<li>
<LinkTo @route="vault.cluster.clients.counts.overview" data-test-tab="overview">
Overview
</LinkTo>
</li>
<li>
<LinkTo @route="vault.cluster.clients.counts.token" data-test-tab="token">
Entity/Non-entity clients
</LinkTo>
</li>
{{#if @showSecretsSync}}
<li>
<LinkTo @route="vault.cluster.clients.counts.sync" data-test-tab="sync">
Secrets sync clients
</LinkTo>
</li>
{{/if}}
<li>
<LinkTo @route="vault.cluster.clients.counts.acme" data-test-tab="acme">
ACME clients
</LinkTo>
</li>
</ul>
</nav>
29 changes: 2 additions & 27 deletions ui/app/components/clients/page/counts.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
{{#if (eq @activity.id "no-data")}}
<Clients::NoData @config={{@config}} @dateRangeMessage={{this.dateRangeMessage}} />
{{else if @activityError}}
<Clients::Error @error={{@activityError}} />
<Clients::Counts::Error @error={{@activityError}} />
{{else}}
{{#if (eq @config.enabled "Off")}}
<Hds::Alert @type="inline" @color="warning" class="has-bottom-margin-s" as |A|>
Expand Down Expand Up @@ -151,32 +151,7 @@
</Hds::Alert>
{{/if}}

<nav class="tabs has-bottom-margin-s" aria-label="navigation for managing client counts">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file & its associated test were getting a bit hairy, so moved it into a separate component 🧹

<ul>
<li>
<LinkTo @route="vault.cluster.clients.counts.overview" data-test-tab="overview">
Overview
</LinkTo>
</li>
<li>
<LinkTo @route="vault.cluster.clients.counts.token" data-test-tab="token">
Entity/Non-entity clients
</LinkTo>
</li>
{{#if this.version.hasSecretsSync}}
<li>
<LinkTo @route="vault.cluster.clients.counts.sync" data-test-tab="sync">
Secrets sync clients
</LinkTo>
</li>
{{/if}}
<li>
<LinkTo @route="vault.cluster.clients.counts.acme" data-test-tab="acme">
ACME clients
</LinkTo>
</li>
</ul>
</nav>
<Clients::Counts::NavBar @showSecretsSync={{this.showSecretsSync}} />

{{! CLIENT COUNT PAGE COMPONENTS RENDER HERE }}
{{yield}}
Expand Down
18 changes: 18 additions & 0 deletions ui/app/components/clients/page/counts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { filterVersionHistory, formatDateObject } from 'core/utils/client-count-
import timestamp from 'core/utils/timestamp';

import type AdapterError from '@ember-data/adapter';
import type FlagsService from 'vault/services/flags';
import type StoreService from 'vault/services/store';
import type VersionService from 'vault/services/version';
import type ClientsActivityModel from 'vault/models/clients/activity';
Expand All @@ -30,6 +31,7 @@ interface Args {
}

export default class ClientsCountsPageComponent extends Component<Args> {
@service declare readonly flags: FlagsService;
@service declare readonly version: VersionService;
@service declare readonly store: StoreService;

Expand Down Expand Up @@ -162,6 +164,22 @@ export default class ClientsCountsPageComponent extends Component<Args> {
return activity?.total;
}

get showSecretsSync(): boolean {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this value is only needed on one component, we don't need to compute this in the route to add to the model. better off calculating it closer to where we actually use the value, where we already have enough context to compute it. in other words, by calculating the value here we can simply pass it straight into the <NavBar /> which is the only place where it's used. 😀

const { activity } = this.args;
// if there is any sync client data, show it
if (activity && activity?.total?.secret_syncs > 0) return true;

// otherwise, show the tab based on the cluster type and license
if (this.version.isCommunity) return false;

const isHvd = this.flags.isHvdManaged;
const onLicense = this.version.hasSecretsSync;

// we can't tell if HVD clusters have the feature or not, so we show it by default
// if the cluster is not HVD, show the tab if the feature is on the license
return isHvd || onLicense;
}

@action
onDateChange(dateObject: { dateType: string; monthIdx: number; year: number }) {
const { dateType, monthIdx, year } = dateObject;
Expand Down
4 changes: 2 additions & 2 deletions ui/app/components/clients/page/overview.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
~}}

<Clients::RunningTotal
@isSecretsSyncActivated={{@isSecretsSyncActivated}}
@isSecretsSyncActivated={{this.flags.secretsSyncIsActivated}}
@byMonthActivityData={{this.byMonthActivityData}}
@isHistoricalMonth={{and (not this.isCurrentMonth) (not this.isDateRange)}}
@isCurrentMonth={{this.isCurrentMonth}}
Expand All @@ -15,7 +15,7 @@
/>
{{#if this.hasAttributionData}}
<Clients::Attribution
@isSecretsSyncActivated={{@isSecretsSyncActivated}}
@isSecretsSyncActivated={{this.flags.secretsSyncIsActivated}}
@totalUsageCounts={{this.totalUsageCounts}}
@newUsageCounts={{this.newClientCounts}}
@totalClientAttribution={{this.totalClientAttribution}}
Expand Down
6 changes: 5 additions & 1 deletion ui/app/components/clients/page/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
*/

import ActivityComponent from '../activity';
import { service } from '@ember/service';
import type FlagsService from 'vault/services/flags';

export default class ClientsOverviewPageComponent extends ActivityComponent {}
export default class ClientsOverviewPageComponent extends ActivityComponent {
@service declare readonly flags: FlagsService;
}
2 changes: 1 addition & 1 deletion ui/app/components/clients/page/sync.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
{{#if @isSecretsSyncActivated}}
{{#if this.flags.secretsSyncIsActivated}}
{{#if (not this.byMonthActivityData)}}
{{! byMonthActivityData is an empty array if there is no monthly data (monthly breakdown was added in 1.11)
this means the user has queried dates before sync clients existed. we render an empty state instead of
Expand Down
5 changes: 4 additions & 1 deletion ui/app/components/clients/page/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
*/

import ActivityComponent from '../activity';

import { service } from '@ember/service';
import type FlagsService from 'vault/services/flags';
export default class SyncComponent extends ActivityComponent {
@service declare readonly flags: FlagsService;

title = 'Secrets sync usage';
description =
'This data can be used to understand how many secrets sync clients have been used for this date range. Each Vault secret that is synced to at least one destination counts as one Vault client.';
Expand Down
44 changes: 7 additions & 37 deletions ui/app/routes/vault/cluster/clients/counts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@

import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { DEBUG } from '@glimmer/env';
import timestamp from 'core/utils/timestamp';
import { getUnixTime } from 'date-fns';

import type FlagsService from 'vault/services/flags';
import type StoreService from 'vault/services/store';
import type VersionService from 'vault/services/version';

import type { ModelFrom } from 'vault/vault/route';
import type ClientsRoute from '../clients';
import type ClientsConfigModel from 'vault/models/clients/config';
import type ClientsActivityModel from 'vault/models/clients/activity';
import type ClientsConfigModel from 'vault/models/clients/config';
import type ClientsCountsController from 'vault/controllers/vault/cluster/clients/counts';
export interface ClientsCountsRouteParams {
start_time?: string | number | undefined;
Expand All @@ -24,14 +23,8 @@ export interface ClientsCountsRouteParams {
mountPath?: string | undefined;
}

interface ActivationFlagsResponse {
data: {
activated: Array<string>;
unactivated: Array<string>;
};
}

export default class ClientsCountsRoute extends Route {
@service declare readonly flags: FlagsService;
@service declare readonly store: StoreService;
@service declare readonly version: VersionService;

Expand All @@ -42,6 +35,10 @@ export default class ClientsCountsRoute extends Route {
mountPath: { refreshModel: false, replace: true },
};

beforeModel() {
return this.flags.fetchActivatedFlags();
}

async getActivity(start_time: number | null, end_time: number) {
let activity, activityError;
// if there is no start_time we want the user to manually choose a date
Expand All @@ -59,30 +56,6 @@ export default class ClientsCountsRoute extends Route {
return { activity, activityError };
}

async getActivatedFeatures() {
try {
const resp: ActivationFlagsResponse = await this.store
.adapterFor('application')
.ajax('/v1/sys/activation-flags', 'GET', { unauthenticated: true, namespace: null });
return resp.data?.activated;
} catch (error) {
if (DEBUG) console.error(error); // eslint-disable-line no-console
return [];
}
}

async isSecretsSyncActivated(activity: ClientsActivityModel | undefined) {
// if there are secrets, the feature is activated
if (activity && activity.total?.secret_syncs > 0) return true;

// if feature is not in license, it's definitely not activated
if (!this.version.hasSecretsSync) return false;

// otherwise check explicitly if the feature has been activated
const activatedFeatures = await this.getActivatedFeatures();
return activatedFeatures.includes('secrets-sync');
}

async model(params: ClientsCountsRouteParams) {
const { config, versionHistory } = this.modelFor('vault.cluster.clients') as ModelFrom<ClientsRoute>;
// only enterprise versions will have a relevant billing start date, if null users must select initial start time
Expand All @@ -95,14 +68,11 @@ export default class ClientsCountsRoute extends Route {
const endTimestamp = Number(params.end_time) || getUnixTime(timestamp.now());
const { activity, activityError } = await this.getActivity(startTimestamp, endTimestamp);

const isSecretsSyncActivated = await this.isSecretsSyncActivated(activity);

return {
activity,
activityError,
config,
endTimestamp,
isSecretsSyncActivated,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea moving this to the counts.ts component! Much clearer there than passing down from this route model hook 🥇

startTimestamp,
versionHistory,
};
Expand Down
1 change: 0 additions & 1 deletion ui/app/templates/vault/cluster/clients/counts.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
@activityError={{this.model.activityError}}
@config={{this.model.config}}
@endTimestamp={{this.model.endTimestamp}}
@isSecretsSyncActivated={{this.model.isSecretsSyncActivated}}
@mountPath={{this.mountPath}}
@namespace={{this.ns}}
@onFilterChange={{this.updateQueryParams}}
Expand Down
1 change: 0 additions & 1 deletion ui/app/templates/vault/cluster/clients/counts/overview.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

<Clients::Page::Overview
@activity={{this.model.activity}}
@isSecretsSyncActivated={{this.model.isSecretsSyncActivated}}
@versionHistory={{this.model.versionHistory}}
@startTimestamp={{this.model.startTimestamp}}
@endTimestamp={{this.model.endTimestamp}}
Expand Down
1 change: 0 additions & 1 deletion ui/app/templates/vault/cluster/clients/counts/sync.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
~}}

<Clients::Page::Sync
@isSecretsSyncActivated={{this.model.isSecretsSyncActivated}}
@activity={{this.model.activity}}
@versionHistory={{this.model.versionHistory}}
@startTimestamp={{this.model.startTimestamp}}
Expand Down
Loading
Loading