mirror of https://github.com/status-im/consul.git
Revert link existing but better 🪦 (#20830)
* Revert "feat: add alert to link to hcp modal to ask a user refresh a page; up… (#20682)" This reverts commitdd833d9a36
. * Revert "chor: change cluster name param to have datacenter.name as default value (#20644)" This reverts commit8425cd0f90
. * Revert "chor: adds informative error message when acls disabled and read-only… (#20600)" This reverts commit9d712ccfc7
. * Revert "Cc 7147 link to hcp modal (#20474)" This reverts commit8c05e57ac1
. * Revert "Add nav bar item to show HCP link status and encourage folks to link (#20370)" This reverts commit22e6ce0df1
. * Revert "Cc 7145 hcp link status api (#20330)" This reverts commit049ca102c4
. * Revert "💜 Cc 7187/purple banner for linking existing clusters (#20275)" This reverts commit5119667cd1
.
This commit is contained in:
parent
cff9161bb5
commit
bfbc0ee4fd
|
@ -1,3 +0,0 @@
|
||||||
```release-note:feature
|
|
||||||
ui: Added a banner to let users link their clusters to HCP
|
|
||||||
```
|
|
|
@ -1,10 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import BaseAbility from './base';
|
|
||||||
|
|
||||||
export default class OperatorAbility extends BaseAbility {
|
|
||||||
resource = 'operator';
|
|
||||||
}
|
|
|
@ -122,9 +122,7 @@
|
||||||
class='hds-side-nav-hide-when-minimized consul-side-nav__selector-group'
|
class='hds-side-nav-hide-when-minimized consul-side-nav__selector-group'
|
||||||
as |SNL|
|
as |SNL|
|
||||||
>
|
>
|
||||||
<DataSource @src={{uri '/${partition}/*/${dc}/hcp-link' (hash dc=@dc partition=@partition name=@dc)}} as |hcpLink|>
|
<HcpNavItem @list={{SNL}}/>
|
||||||
<HcpNavItem @list={{SNL}} @linkData={{hcpLink.data}} />
|
|
||||||
</DataSource>
|
|
||||||
<Consul::Datacenter::Selector
|
<Consul::Datacenter::Selector
|
||||||
@list={{SNL}}
|
@list={{SNL}}
|
||||||
@dc={{@dc}}
|
@dc={{@dc}}
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default class HashiCorpConsul extends Component {
|
||||||
@service('env') env;
|
@service('env') env;
|
||||||
|
|
||||||
get consulVersion() {
|
get consulVersion() {
|
||||||
const suffix = this.env.isEnterprise ? '+ent' : '';
|
const suffix = !['', 'oss'].includes(this.env.var('CONSUL_BINARY_TYPE')) ? '+ent' : '';
|
||||||
return `${this.env.var('CONSUL_VERSION')}${suffix}`;
|
return `${this.env.var('CONSUL_VERSION')}${suffix}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,24 +11,5 @@
|
||||||
@isHrefExternal={{true}}
|
@isHrefExternal={{true}}
|
||||||
data-test-back-to-hcp
|
data-test-back-to-hcp
|
||||||
/>
|
/>
|
||||||
{{else}}
|
|
||||||
{{#if this.shouldDisplayNavLinkItem}}
|
|
||||||
{{#if this.alreadyLinked}}
|
|
||||||
<SNL.Link
|
|
||||||
@text="HCP Consul Central"
|
|
||||||
@href={{hcp-resource-id-to-link @linkData.resourceId}}
|
|
||||||
@isHrefExternal={{true}}
|
|
||||||
@badge="Linked"
|
|
||||||
data-test-linked-cluster-hcp-link
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<SNL.Item data-test-link-to-hcp>
|
|
||||||
<button type="button" class="hds-side-nav__list-item-link hcp-nav-item" {{on "click" this.onLinkToConsulCentral}}>
|
|
||||||
<Hds::Text::Body @size='200'>Link to HCP Consul Central</Hds::Text::Body>
|
|
||||||
<FlightIcon class='w-4 h-4' @size='24' @name='arrow-right'/>
|
|
||||||
</button>
|
|
||||||
</SNL.Item>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/let}}
|
{{/let}}
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the user has accessed consul from HCP managed consul, we do NOT want to display the
|
* If the user has accessed consul from HCP managed consul, we do NOT want to display the
|
||||||
|
@ -13,46 +12,10 @@ import { action } from '@ember/object';
|
||||||
*/
|
*/
|
||||||
export default class HcpLinkItemComponent extends Component {
|
export default class HcpLinkItemComponent extends Component {
|
||||||
@service env;
|
@service env;
|
||||||
@service('hcp-link-status') hcpLinkStatus;
|
|
||||||
@service('hcp-link-modal') hcpLinkModal;
|
|
||||||
|
|
||||||
get alreadyLinked() {
|
|
||||||
return this.args.linkData?.isLinked;
|
|
||||||
}
|
|
||||||
|
|
||||||
get shouldDisplayNavLinkItem() {
|
|
||||||
const alreadyLinked = this.alreadyLinked;
|
|
||||||
const undefinedResourceId = !this.args.linkData?.resourceId;
|
|
||||||
const unauthorizedToLink = !this.hcpLinkStatus.hasPermissionToLink;
|
|
||||||
const undefinedLinkStatus = this.args.linkData?.isLinked === undefined;
|
|
||||||
|
|
||||||
// We need permission to link to display the link nav item
|
|
||||||
if (unauthorizedToLink) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the link status is undefined, we don't want to display the link nav item
|
|
||||||
if (undefinedLinkStatus) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user has already linked, but we don't have the resourceId to link them to HCP, we don't want to display the link nav item
|
|
||||||
if (alreadyLinked && undefinedResourceId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// With the death of Consul Central, we don't want to display the link nav item
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get shouldShowBackToHcpItem() {
|
get shouldShowBackToHcpItem() {
|
||||||
const isConsulHcpUrlDefined = !!this.env.var('CONSUL_HCP_URL');
|
const isConsulHcpUrlDefined = !!this.env.var('CONSUL_HCP_URL');
|
||||||
const isConsulHcpEnabled = !!this.env.var('CONSUL_HCP_ENABLED');
|
const isConsulHcpEnabled = !!this.env.var('CONSUL_HCP_ENABLED');
|
||||||
return isConsulHcpEnabled && isConsulHcpUrlDefined;
|
return isConsulHcpEnabled && isConsulHcpUrlDefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
onLinkToConsulCentral() {
|
|
||||||
this.hcpLinkModal.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
{{!
|
|
||||||
Copyright (c) HashiCorp, Inc.
|
|
||||||
SPDX-License-Identifier: BUSL-1.1
|
|
||||||
}}
|
|
||||||
{{#if (and this.hcpLinkStatus.shouldDisplayBanner this.notLinked)}}
|
|
||||||
<Hds::Alert @type="page" @color="highlight" @onDismiss={{this.onDismiss}} class="link-to-hcp-banner"
|
|
||||||
data-test-link-to-hcp-banner as |A|>
|
|
||||||
<A.Title data-test-link-to-hcp-banner-title>{{t "components.link-to-hcp-banner.title"}}</A.Title>
|
|
||||||
<A.Description data-test-link-to-hcp-banner-description>{{t "components.link-to-hcp-banner.description"
|
|
||||||
isEnterprise=this.env.isEnterprise}}
|
|
||||||
</A.Description>
|
|
||||||
<A.Button @text={{t "components.link-to-hcp-banner.clusterLinkButton"}} @color="secondary" {{on "click"
|
|
||||||
this.onClusterLink}}
|
|
||||||
data-test-link-to-hcp-banner-button/>
|
|
||||||
<A.Link::Standalone @color="secondary" @icon="docs-link" @iconPosition="trailing" @text={{t
|
|
||||||
"components.link-to-hcp-banner.viewDocumentation"}}
|
|
||||||
@href="https://developer.hashicorp.com/hcp/docs/consul/self-managed"
|
|
||||||
data-test-link-to-hcp-banner-view-documentation/>
|
|
||||||
<A.Link::Standalone @color="secondary" @icon="docs-link" @iconPosition="trailing"
|
|
||||||
@text={{t "components.link-to-hcp-banner.consulCentralDocumentation"}}
|
|
||||||
@href="https://developer.hashicorp.com/hcp/docs/consul/concepts/consul-central"
|
|
||||||
data-test-link-to-hcp-banner-consul-central-documentation/>
|
|
||||||
</Hds::Alert>
|
|
||||||
{{/if}}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Component from '@glimmer/component';
|
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
|
|
||||||
export default class LinkToHcpBannerComponent extends Component {
|
|
||||||
@service('hcp-link-status') hcpLinkStatus;
|
|
||||||
@service('hcp-link-modal') hcpLinkModal;
|
|
||||||
@service('env') env;
|
|
||||||
|
|
||||||
get notLinked() {
|
|
||||||
return this.args.linkData?.isLinked === false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onDismiss() {
|
|
||||||
this.hcpLinkStatus.dismissHcpLinkBanner();
|
|
||||||
}
|
|
||||||
@action
|
|
||||||
onClusterLink() {
|
|
||||||
this.hcpLinkModal.show();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
{{!
|
|
||||||
Copyright (c) HashiCorp, Inc.
|
|
||||||
SPDX-License-Identifier: BUSL-1.1
|
|
||||||
}}
|
|
||||||
<DataSource @src={{uri '/${partition}/${nspace}/${dc}/policy/00000000-0000-0000-0000-000000000002'
|
|
||||||
(hash dc=@dc partition=@partition nspace=@nspace) }} as |globalReadonlyPolicy|>
|
|
||||||
<Hds::Modal id="link-to-hcp-modal" class="link-to-hcp-modal" data-test-link-to-hcp-modal
|
|
||||||
@onClose={{fn this.deactivateModal}} as |M|>
|
|
||||||
<M.Header>
|
|
||||||
Link to HCP Consul Central
|
|
||||||
</M.Header>
|
|
||||||
<M.Body>
|
|
||||||
{{#if (not (can "read acls"))}}
|
|
||||||
<Hds::Alert class="link-to-hcp-modal__no-acls-alert" @type="inline" @color="critical" data-test-link-to-hcp-modal-no-acls-alert as |A|>
|
|
||||||
<A.Title>ACLs are disabled on this cluster.</A.Title>
|
|
||||||
<A.Description>The cluster can only be linked with read/write access.</A.Description>
|
|
||||||
</Hds::Alert>
|
|
||||||
{{/if}}
|
|
||||||
<Hds::Form::Radio::Group data-test-link-to-hcp-modal-access-level-options @layout="vertical" @name="accessMode" as
|
|
||||||
|G|>
|
|
||||||
<G.Legend>Select cluster access mode before linking</G.Legend>
|
|
||||||
<G.HelperText>Control the level of access that HCP Consul Central has to your linked cluster.
|
|
||||||
<Hds::Link::Inline @href="https://developer.hashicorp.com/hcp/docs/consul/concepts/cluster-permissions" @isHrefExternal={{true}}
|
|
||||||
@color="secondary">Learn more
|
|
||||||
</Hds::Link::Inline>
|
|
||||||
</G.HelperText>
|
|
||||||
<G.Radio::Field @id="accessMode-management" checked @value={{this.AccessLevel.GLOBALREADWRITE}} {{on "change"
|
|
||||||
this.onAccessModeChanged}}
|
|
||||||
as |F|>
|
|
||||||
<F.Label>Read/write</F.Label>
|
|
||||||
<F.HelperText>HCP Consul Central can perform write operations on your cluster (i.e. cluster peering).
|
|
||||||
</F.HelperText>
|
|
||||||
</G.Radio::Field>
|
|
||||||
<G.Radio::Field @id="accessMode-readonly" @value={{this.AccessLevel.GLOBALREADONLY}} {{on "change"
|
|
||||||
this.onAccessModeChanged}}
|
|
||||||
as |F|>
|
|
||||||
<F.Label>Read-only</F.Label>
|
|
||||||
<F.HelperText>HCP Consul Central can only read information from your cluster. Read-only requires an ACL token
|
|
||||||
with the “builtin/global-read-only” policy in the next step.
|
|
||||||
</F.HelperText>
|
|
||||||
</G.Radio::Field>
|
|
||||||
{{#if (and this.isReadOnlyAccessLevelSelected (not (can "read acls")))}}
|
|
||||||
<G.Error data-test-link-to-hcp-modal-access-level-options-error>ACLs are disabled on this cluster and are required for read-only access.</G.Error>
|
|
||||||
{{/if}}
|
|
||||||
</Hds::Form::Radio::Group>
|
|
||||||
{{#if (and this.isReadOnlyAccessLevelSelected (can "read acls") (can "create tokens"))}}
|
|
||||||
<div class="link-to-hcp-modal__generate-token">
|
|
||||||
{{#if (and globalReadonlyPolicy globalReadonlyPolicy.data)}}
|
|
||||||
<p class="hds-typography-display-100 hds-font-weight-medium font-family-sans-display">
|
|
||||||
Generate a read-only ACL token now (preferred) or copy an existing token’s secret ID
|
|
||||||
</p>
|
|
||||||
{{#if this.isTokenGenerated}}
|
|
||||||
<Hds::Card::Container data-test-link-to-hcp-modal-generate-token-card
|
|
||||||
class="link-to-hcp-modal__generate-token__copy-card"
|
|
||||||
@level="mid"
|
|
||||||
@hasBorder={{true}}>
|
|
||||||
<div>
|
|
||||||
<p class="hds-font-weight-semibold">Token secret ID</p>
|
|
||||||
<p class="hds-typography-code-200 link-to-hcp-modal__generate-token__copy-card__token"
|
|
||||||
data-test-link-to-hcp-modal-generate-token-card-value
|
|
||||||
id="tokenSecretId">
|
|
||||||
{{this.token}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Hds::Copy::Button
|
|
||||||
@text="Copy"
|
|
||||||
data-test-link-to-hcp-modal-generate-token-card-copy-button
|
|
||||||
@isIconOnly={{true}}
|
|
||||||
@targetToCopy="#tokenSecretId" />
|
|
||||||
|
|
||||||
</Hds::Card::Container>
|
|
||||||
{{else}}
|
|
||||||
<div>
|
|
||||||
<Hds::Button
|
|
||||||
data-test-link-to-hcp-modal-generate-token-button
|
|
||||||
@color="tertiary"
|
|
||||||
@text={{if this.isGeneratingToken "Generating token" "Generate a read-only ACL token"}}
|
|
||||||
@icon={{if this.isGeneratingToken "loading" "token"}}
|
|
||||||
@disabled={{this.isGeneratingToken}}
|
|
||||||
{{on "click" (fn this.onGenerateTokenClicked globalReadonlyPolicy)}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{else}}
|
|
||||||
<Hds::Alert @type="compact" data-test-link-to-hcp-modal-missed-policy-alert as |A|>
|
|
||||||
<A.Description>Could not generate token.</A.Description>
|
|
||||||
</Hds::Alert>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<Hds::Alert class="link-to-hcp-modal__refresh-page-alert" @type="compact" data-test-link-to-hcp-modal-refresh-page-alert as |A|>
|
|
||||||
<A.Description>After you link your cluster to HCP, close this modal and refresh the page.</A.Description>
|
|
||||||
</Hds::Alert>
|
|
||||||
</M.Body>
|
|
||||||
<M.Footer as |F|>
|
|
||||||
<Hds::ButtonSet>
|
|
||||||
<Hds::Button type="button"
|
|
||||||
@text="Next: Authenticate into HCP"
|
|
||||||
@icon="external-link"
|
|
||||||
@iconPosition="trailing"
|
|
||||||
data-test-link-to-hcp-modal-next-button
|
|
||||||
@href={{hcp-authentication-link @dc this.accessLevel}}
|
|
||||||
/>
|
|
||||||
<Hds::Button type="button" @text="Cancel" @color="secondary"
|
|
||||||
data-test-link-to-hcp-modal-cancel-button
|
|
||||||
{{on "click" F.close}}
|
|
||||||
/>
|
|
||||||
</Hds::ButtonSet>
|
|
||||||
</M.Footer>
|
|
||||||
</Hds::Modal>
|
|
||||||
</DataSource>
|
|
|
@ -1,66 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Component from '@glimmer/component';
|
|
||||||
import { tracked } from '@glimmer/tracking';
|
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
|
|
||||||
export const ACCESS_LEVEL = {
|
|
||||||
GLOBALREADONLY: 'CONSUL_ACCESS_LEVEL_GLOBAL_READ_ONLY',
|
|
||||||
GLOBALREADWRITE: 'CONSUL_ACCESS_LEVEL_GLOBAL_READ_WRITE',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class LinkToHcpModalComponent extends Component {
|
|
||||||
@service('repository/token') tokenRepo;
|
|
||||||
@service('repository/policy') policyRepo;
|
|
||||||
@service('hcp-link-modal') hcpLinkModal;
|
|
||||||
@service('router') router;
|
|
||||||
|
|
||||||
@tracked
|
|
||||||
token = '';
|
|
||||||
@tracked
|
|
||||||
accessLevel = ACCESS_LEVEL.GLOBALREADWRITE;
|
|
||||||
@tracked
|
|
||||||
isGeneratingToken = false;
|
|
||||||
AccessLevel = ACCESS_LEVEL;
|
|
||||||
|
|
||||||
constructor(args, owner) {
|
|
||||||
super(...arguments);
|
|
||||||
// it is needed for .lookup to not flakey in tests
|
|
||||||
this.hideModal = this.hcpLinkModal.hide.bind(this.hcpLinkModal);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isReadOnlyAccessLevelSelected() {
|
|
||||||
return this.accessLevel === this.AccessLevel.GLOBALREADONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isTokenGenerated() {
|
|
||||||
return this.token && this.token.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
onGenerateTokenClicked = (policy) => {
|
|
||||||
this.isGeneratingToken = true;
|
|
||||||
let token = this.tokenRepo.create({
|
|
||||||
Datacenter: this.args.dc,
|
|
||||||
Partition: this.args.partition,
|
|
||||||
Namespace: this.args.nspace,
|
|
||||||
Policies: [policy.data],
|
|
||||||
});
|
|
||||||
this.tokenRepo.persist(token, event).then((token) => {
|
|
||||||
this.token = token.SecretID;
|
|
||||||
this.isGeneratingToken = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
@action
|
|
||||||
onAccessModeChanged({ target }) {
|
|
||||||
this.accessLevel = target.value;
|
|
||||||
}
|
|
||||||
@action
|
|
||||||
deactivateModal() {
|
|
||||||
this.hideModal();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
.link-to-hcp-modal {
|
|
||||||
&__no-acls-alert {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
&__refresh-page-alert {
|
|
||||||
margin-top: 16px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
&__generate-token {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
margin-top: 8px;
|
|
||||||
|
|
||||||
&__copy-card {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: start;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 16px 24px;
|
|
||||||
|
|
||||||
&__token {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,6 @@ export default class ApplicationController extends Controller {
|
||||||
@service('router') router;
|
@service('router') router;
|
||||||
@service('store') store;
|
@service('store') store;
|
||||||
@service('feedback') feedback;
|
@service('feedback') feedback;
|
||||||
@service('hcp-link-modal') hcpLinkModal;
|
|
||||||
|
|
||||||
// TODO: We currently do this in the controller instead of the router
|
// TODO: We currently do this in the controller instead of the router
|
||||||
// as the nspace and dc variables aren't available directly on the Route
|
// as the nspace and dc variables aren't available directly on the Route
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Helper from '@ember/component/helper';
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A HCP URL looks like:
|
|
||||||
* https://portal.cloud.hashicorp.com/services/consul/clusters/self-managed/link-existing?cluster_name=test-from-api&cluster_version=1.18.0&cluster_access_mode=CONSUL_ACCESS_LEVEL_GLOBAL_READ_WRITE&redirect_url=localhost:8500/services
|
|
||||||
*/
|
|
||||||
export const HCP_PREFIX =
|
|
||||||
'https://portal.cloud.hashicorp.com/services/consul/clusters/self-managed/link-existing';
|
|
||||||
export default class hcpAuthenticationLink extends Helper {
|
|
||||||
@service('env') env;
|
|
||||||
compute([datacenterName, accessMode]) {
|
|
||||||
let url = new URL(HCP_PREFIX);
|
|
||||||
const clusterVersion = this.env.var('CONSUL_VERSION');
|
|
||||||
|
|
||||||
if (datacenterName) {
|
|
||||||
url.searchParams.append('cluster_name', datacenterName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clusterVersion) {
|
|
||||||
url.searchParams.append('cluster_version', clusterVersion);
|
|
||||||
}
|
|
||||||
if (accessMode) {
|
|
||||||
url.searchParams.append('cluster_access_mode', accessMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return url.toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Helper from '@ember/component/helper';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A resourceId Looks like:
|
|
||||||
* organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api
|
|
||||||
* organization/${organizationId}/project/${projectId}/hashicorp.consul.global-network-manager.cluster/${clusterName}
|
|
||||||
*
|
|
||||||
* A HCP URL looks like:
|
|
||||||
* https://portal.hcp.dev/services/consul/clusters/self-managed/test-from-api?project_id=4b09958c-fa91-43ab-8029-eb28d8cee9d4
|
|
||||||
* ${HCP_PREFIX}/${clusterName}?project_id=${projectId}
|
|
||||||
*/
|
|
||||||
export const HCP_PREFIX =
|
|
||||||
'https://portal.cloud.hashicorp.com/services/consul/clusters/self-managed';
|
|
||||||
export default class hcpResourceIdToLink extends Helper {
|
|
||||||
// TODO: How can we figure out different HCP environments?
|
|
||||||
compute([resourceId], hash) {
|
|
||||||
let url = HCP_PREFIX;
|
|
||||||
// Array looks like: ["organization", organizationId, "project", projectId, "hashicorp.consul.global-network-manager.cluster", "Cluster Id"]
|
|
||||||
const [, , , projectId, , clusterName] = resourceId.split('/');
|
|
||||||
if (!projectId || !clusterName) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
url += `/${clusterName}?project_id=${projectId}`;
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,9 +7,6 @@ import Service from '@ember/service';
|
||||||
import { env } from 'consul-ui/env';
|
import { env } from 'consul-ui/env';
|
||||||
|
|
||||||
export default class EnvService extends Service {
|
export default class EnvService extends Service {
|
||||||
get isEnterprise() {
|
|
||||||
return !['', 'oss'].includes(this.var('CONSUL_BINARY_TYPE'));
|
|
||||||
}
|
|
||||||
// deprecated
|
// deprecated
|
||||||
// TODO: Remove this elsewhere in the app and use var instead
|
// TODO: Remove this elsewhere in the app and use var instead
|
||||||
env(key) {
|
env(key) {
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Service from '@ember/service';
|
|
||||||
import { tracked } from '@glimmer/tracking';
|
|
||||||
|
|
||||||
export default class HcpLinkModalService extends Service {
|
|
||||||
@tracked isModalVisible = false;
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.isModalVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hide() {
|
|
||||||
this.isModalVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Service, { inject as service } from '@ember/service';
|
|
||||||
import { tracked } from '@glimmer/tracking';
|
|
||||||
|
|
||||||
const LOCAL_STORAGE_KEY = 'consul:hideHcpLinkBanner';
|
|
||||||
|
|
||||||
export default class HcpLinkStatus extends Service {
|
|
||||||
@service abilities;
|
|
||||||
@tracked
|
|
||||||
userDismissedBanner = false;
|
|
||||||
|
|
||||||
get shouldDisplayBanner() {
|
|
||||||
// With the death of Consul Central, we don't want to display the link nav item
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasPermissionToLink() {
|
|
||||||
return this.abilities.can('write operators') && this.abilities.can('write acls');
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.userDismissedBanner = !!localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
userHasLinked() {
|
|
||||||
// TODO: CC-7145 - once can fetch the link status from the backend, fetch it and set it here
|
|
||||||
}
|
|
||||||
|
|
||||||
dismissHcpLinkBanner() {
|
|
||||||
localStorage.setItem(LOCAL_STORAGE_KEY, true);
|
|
||||||
this.userDismissedBanner = true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import RepositoryService from 'consul-ui/services/repository';
|
|
||||||
import dataSource from 'consul-ui/decorators/data-source';
|
|
||||||
|
|
||||||
export default class HcpLinkService extends RepositoryService {
|
|
||||||
/**
|
|
||||||
* Data looks like
|
|
||||||
* {
|
|
||||||
* "data": {
|
|
||||||
* "clientId": "5wZyAPvDFbgDdO3439m8tufwO9hElphu",
|
|
||||||
* "clientSecret": "SWX0XShcp3doc7RF8YCjJ-WATyeMAjFaf1eA0mnzlNHLF4IXbFz6xyjSZvHzAR_i",
|
|
||||||
* "resourceId": "organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api"
|
|
||||||
* },
|
|
||||||
* "generation": "01HMSDHXQTCQGD3Z68B3H58YFE",
|
|
||||||
* "id": {
|
|
||||||
* "name": "global",
|
|
||||||
* "tenancy": {
|
|
||||||
* "peerName": "local"
|
|
||||||
* },
|
|
||||||
* "type": {
|
|
||||||
* "group": "hcp",
|
|
||||||
* "groupVersion": "v2",
|
|
||||||
* "kind": "Link"
|
|
||||||
* },
|
|
||||||
* "uid": "01HMSDHXQTCQGD3Z68B10WBWHX"
|
|
||||||
* },
|
|
||||||
* "status": {
|
|
||||||
* "consul.io/hcp/link": {
|
|
||||||
* "conditions": [
|
|
||||||
* {
|
|
||||||
* "message": "Failed to link to HCP",
|
|
||||||
* "reason": "FAILED",
|
|
||||||
* "state": "STATE_FALSE",
|
|
||||||
* "type": "linked"
|
|
||||||
* }
|
|
||||||
* ],
|
|
||||||
* "observedGeneration": "01HMSDHXQTCQGD3Z68B3H58YFE",
|
|
||||||
* "updatedAt": "2024-01-22T20:24:57.141144170Z"
|
|
||||||
* }
|
|
||||||
* },
|
|
||||||
* "version": "57"
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
@dataSource('/:partition/:ns/:dc/hcp-link')
|
|
||||||
async fetch({ partition, ns, dc }, { uri }, request) {
|
|
||||||
let result;
|
|
||||||
try {
|
|
||||||
result = (
|
|
||||||
await request`
|
|
||||||
GET /api/hcp/v2/link/global
|
|
||||||
`
|
|
||||||
)((headers, body) => {
|
|
||||||
const isLinked = (body.status['consul.io/hcp/link']['conditions'] || []).some(
|
|
||||||
(condition) => condition.type === 'linked' && condition.state === 'STATE_TRUE'
|
|
||||||
);
|
|
||||||
const resourceId = body.data?.resourceId;
|
|
||||||
|
|
||||||
return {
|
|
||||||
meta: {
|
|
||||||
version: 2,
|
|
||||||
uri: uri,
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
isLinked,
|
|
||||||
resourceId,
|
|
||||||
},
|
|
||||||
headers,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
// set linked to false if the global link is not found
|
|
||||||
if (e.statusCode === 404) {
|
|
||||||
result = Promise.resolve({ isLinked: false });
|
|
||||||
} else {
|
|
||||||
result = Promise.resolve(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -74,7 +74,6 @@
|
||||||
@import 'consul-ui/components/tab-nav';
|
@import 'consul-ui/components/tab-nav';
|
||||||
@import 'consul-ui/components/search-bar';
|
@import 'consul-ui/components/search-bar';
|
||||||
@import 'consul-ui/components/copyable-code';
|
@import 'consul-ui/components/copyable-code';
|
||||||
@import 'consul-ui/components/link-to-hcp-modal';
|
|
||||||
|
|
||||||
@import 'consul-ui/components/consul/loader';
|
@import 'consul-ui/components/consul/loader';
|
||||||
@import 'consul-ui/components/consul/tomography/graph';
|
@import 'consul-ui/components/consul/tomography/graph';
|
||||||
|
|
|
@ -18,9 +18,6 @@ html[data-route^='dc.services.instance'] .app-view > header dl {
|
||||||
margin-bottom: 23px;
|
margin-bottom: 23px;
|
||||||
margin-right: 50px;
|
margin-right: 50px;
|
||||||
}
|
}
|
||||||
html[data-route^='dc.services.index'] .link-to-hcp-banner {
|
|
||||||
margin: 0 -48px;
|
|
||||||
}
|
|
||||||
html[data-route^='dc.services.instance'] .app-view > header dt {
|
html[data-route^='dc.services.instance'] .app-view > header dt {
|
||||||
font-weight: var(--token-typography-font-weight-bold);
|
font-weight: var(--token-typography-font-weight-bold);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,18 +48,18 @@
|
||||||
(and (not (env 'CONSUL_V2_CATALOG_ENABLED')) (not-eq route.currentName 'oauth-provider-debug'))
|
(and (not (env 'CONSUL_V2_CATALOG_ENABLED')) (not-eq route.currentName 'oauth-provider-debug'))
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{! redirect if we aren't on a URL with dc information }}
|
{{! redirect if we aren't on a URL with dc information }}
|
||||||
{{#if (eq route.currentName 'index')}}
|
{{#if (eq route.currentName 'index')}}
|
||||||
{{! until we get to the dc route we don't know any permissions }}
|
{{! until we get to the dc route we don't know any permissions }}
|
||||||
{{! as we don't know the dc, any inital permission based }}
|
{{! as we don't know the dc, any inital permission based }}
|
||||||
{{! redirects are in the dc.show route}}
|
{{! redirects are in the dc.show route}}
|
||||||
|
|
||||||
{{! 2022-04-15: Temporarily reverting the services page to the default }}
|
{{! 2022-04-15: Temporarily reverting the services page to the default }}
|
||||||
{{did-insert
|
{{did-insert
|
||||||
(route-action 'replaceWith' 'dc.services.index' (hash dc=(env 'CONSUL_DATACENTER_LOCAL')))
|
(route-action 'replaceWith' 'dc.services.index' (hash dc=(env 'CONSUL_DATACENTER_LOCAL')))
|
||||||
}}
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{! If we are notfound, guess the params we need }}
|
{{! If we are notfound, guess the params we need }}
|
||||||
{{#if (eq route.currentName 'notfound')}}
|
{{#if (eq route.currentName 'notfound')}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{uri '/*/*/*/notfound/${path}' (hash path=route.params.notfound)}}
|
@src={{uri '/*/*/*/notfound/${path}' (hash path=route.params.notfound)}}
|
||||||
|
@ -75,11 +75,11 @@
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
(if (can 'use nspaces') (or route.params.nspace notfound.nspace token.Namespace '') '')
|
(if (can 'use nspaces') (or route.params.nspace notfound.nspace token.Namespace '') '')
|
||||||
as |partition nspace|
|
as |partition nspace|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{! Make sure we have enough to show the app chrome}}
|
{{! Make sure we have enough to show the app chrome}}
|
||||||
{{! Don't show anything until we have a list of DCs }}
|
{{! Don't show anything until we have a list of DCs }}
|
||||||
<DataSource @src={{uri '/*/*/*/datacenters'}} as |dcs|>
|
<DataSource @src={{uri '/*/*/*/datacenters'}} as |dcs|>
|
||||||
{{! Once we have a list of DCs make sure the DC we are asking for exists }}
|
{{! Once we have a list of DCs make sure the DC we are asking for exists }}
|
||||||
{{! If not use the DC that the UI is running in }}
|
{{! If not use the DC that the UI is running in }}
|
||||||
|
@ -90,60 +90,57 @@
|
||||||
(hash Name=(env 'CONSUL_DATACENTER_LOCAL'))
|
(hash Name=(env 'CONSUL_DATACENTER_LOCAL'))
|
||||||
)
|
)
|
||||||
dcs.data
|
dcs.data
|
||||||
as |dc dcs|
|
as |dc dcs|
|
||||||
}}
|
}}
|
||||||
{{#if (and (gt dc.Name.length 0) dcs)}}
|
{{#if (and (gt dc.Name.length 0) dcs)}}
|
||||||
{{#if this.hcpLinkModal.isModalVisible}}
|
|
||||||
<LinkToHcpModal @dc={{dc.Name}} @nspace={{nspace}} @partition={{partition}}/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{! figure out our current DC and convert it to a model }}
|
{{! figure out our current DC and convert it to a model }}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{uri
|
@src={{uri
|
||||||
'/${partition}/*/${dc}/datacenter-cache/${name}'
|
'/${partition}/*/${dc}/datacenter-cache/${name}'
|
||||||
(hash dc=dc.Name partition=partition name=dc.Name)
|
(hash dc=dc.Name partition=partition name=dc.Name)
|
||||||
}}
|
}}
|
||||||
as |dc|
|
as |dc|
|
||||||
>
|
>
|
||||||
{{#if dc.data}}
|
{{#if dc.data}}
|
||||||
<HashicorpConsul
|
<HashicorpConsul
|
||||||
id='wrapper'
|
id='wrapper'
|
||||||
@dcs={{dcs}}
|
@dcs={{dcs}}
|
||||||
@dc={{dc.data}}
|
@dc={{dc.data}}
|
||||||
@partition={{partition}}
|
@partition={{partition}}
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
@user={{hash token=token}}
|
@user={{hash token=token}}
|
||||||
@onchange={{action 'reauthorize'}}
|
@onchange={{action 'reauthorize'}}
|
||||||
as |consul|
|
as |consul|
|
||||||
>
|
>
|
||||||
|
|
||||||
{{#if error}}
|
{{#if error}}
|
||||||
{{! If we got an error from anything, show an error page }}
|
{{! If we got an error from anything, show an error page }}
|
||||||
<AppError @error={{error}} @login={{consul.login.open}} />
|
<AppError @error={{error}} @login={{consul.login.open}} />
|
||||||
{{else}}
|
{{else}}
|
||||||
{{! Otherwise show the rest of the app}}
|
{{! Otherwise show the rest of the app}}
|
||||||
<Outlet
|
<Outlet
|
||||||
@name='application'
|
@name='application'
|
||||||
@model={{hash app=consul user=(hash token=token) dc=dc.data dcs=dcs}}
|
@model={{hash app=consul user=(hash token=token) dc=dc.data dcs=dcs}}
|
||||||
as |o|
|
as |o|
|
||||||
>
|
>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</Outlet>
|
</Outlet>
|
||||||
|
|
||||||
{{! loading component for when we need it}}
|
{{! loading component for when we need it}}
|
||||||
<Consul::Loader class='view-loader' />
|
<Consul::Loader class='view-loader' />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</HashicorpConsul>
|
</HashicorpConsul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</DataSource>
|
</DataSource>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</DataSource>
|
</DataSource>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{! Routes with no main navigation }}
|
{{! Routes with no main navigation }}
|
||||||
<Outlet @name='application' @model={{hash user=(hash token=token)}} as |o|>
|
<Outlet @name='application' @model={{hash user=(hash token=token)}} as |o|>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</Outlet>
|
</Outlet>
|
||||||
|
|
|
@ -62,11 +62,7 @@ as |route|>
|
||||||
(or route.params.nspace route.model.user.token.Namespace 'default')
|
(or route.params.nspace route.model.user.token.Namespace 'default')
|
||||||
|
|
||||||
as |sort filters items partition nspace|}}
|
as |sort filters items partition nspace|}}
|
||||||
{{#let route.params.dc as |dc|}}
|
|
||||||
<DataSource @src={{uri '/${partition}/*/${dc}/hcp-link' (hash dc=dc partition=partition name=dc) }} as |hcpLink|>
|
|
||||||
<LinkToHcpBanner @linkData={{hcpLink.data}}/>
|
|
||||||
</DataSource>
|
|
||||||
{{/let}}
|
|
||||||
<AppView>
|
<AppView>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"clientId": "5wZyAPvDFbgDdO3439m8tufwO9hElphu",
|
|
||||||
"clientSecret": "SWX0XShcp3doc7RF8YCjJ-WATyeMAjFaf1eA0mnzlNHLF4IXbFz6xyjSZvHzAR_i",
|
|
||||||
"resourceId": "organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"consul.io/hcp/link": {
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"message": "Successfully linked to cluster 'organization/f53e5646-6529-4698-ae29-d74f8bd22a01/project/6994bb7a-5561-4d5c-8bb0-cf40177e5b77/hashicorp.consul.global-network-manager.cluster/mkam-vm'",
|
|
||||||
"reason": "SUCCESS",
|
|
||||||
"state": "STATE_FALSE",
|
|
||||||
"type": "linked"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"observedGeneration":"01HMA2VPHVKNF6QR8TD07KDN5K",
|
|
||||||
"updatedAt":"2024-01-16T21:29:25.923140Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { module, test } from 'qunit';
|
|
||||||
import { click, visit } from '@ember/test-helpers';
|
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
|
||||||
|
|
||||||
const bannerSelector = '[data-test-link-to-hcp-banner]';
|
|
||||||
const linkToHcpSelector = '[data-test-link-to-hcp]';
|
|
||||||
const linkToHcpBannerButtonSelector = '[data-test-link-to-hcp-banner-button]';
|
|
||||||
const linkToHcpModalSelector = '[data-test-link-to-hcp-modal]';
|
|
||||||
const linkToHcpModalCancelButtonSelector = '[data-test-link-to-hcp-modal-cancel-button]';
|
|
||||||
module.skip('Acceptance | link to hcp', function (hooks) {
|
|
||||||
setupApplicationTest(hooks);
|
|
||||||
|
|
||||||
hooks.beforeEach(function () {
|
|
||||||
// clear local storage so we don't have any settings
|
|
||||||
window.localStorage.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the banner and nav item are initially displayed on services page', async function (assert) {
|
|
||||||
// default route is services page so we're good here
|
|
||||||
await visit('/');
|
|
||||||
// Expect the banner to be visible by default
|
|
||||||
assert.dom(bannerSelector).isVisible('Banner is visible by default');
|
|
||||||
// expect linkToHCP nav item to be visible as well
|
|
||||||
assert.dom(linkToHcpSelector).isVisible('Link to HCP nav item is visible by default');
|
|
||||||
// Click on the dismiss button
|
|
||||||
await click(`${bannerSelector} button[aria-label="Dismiss"]`);
|
|
||||||
assert.dom(bannerSelector).doesNotExist('Banner is gone after dismissing');
|
|
||||||
// link to HCP nav item still there
|
|
||||||
assert.dom(linkToHcpSelector).isVisible('Link to HCP nav item is visible by default');
|
|
||||||
// Refresh the page
|
|
||||||
await visit('/');
|
|
||||||
assert.dom(bannerSelector).doesNotExist('Banner is still gone after refresh');
|
|
||||||
// link to HCP nav item still there
|
|
||||||
assert.dom(linkToHcpSelector).isVisible('Link to HCP nav item is visible by default');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the link to hcp modal window appears when trigger from side-nav item and from banner', async function (assert) {
|
|
||||||
// default route is services page so we're good here
|
|
||||||
await visit('/');
|
|
||||||
// Expect the banner to be visible by default
|
|
||||||
assert.dom(bannerSelector).isVisible('Banner is visible by default');
|
|
||||||
// expect linkToHCP nav item to be visible as well
|
|
||||||
assert.dom(linkToHcpSelector).isVisible('Link to HCP nav item is visible by default');
|
|
||||||
// Click on the link to HCP banner button
|
|
||||||
await click(`${bannerSelector} ${linkToHcpBannerButtonSelector}`);
|
|
||||||
|
|
||||||
// link to HCP modal appears
|
|
||||||
assert.dom(linkToHcpModalSelector).isVisible('Link to HCP modal is visible');
|
|
||||||
// Click on the cancel button
|
|
||||||
await click(`${linkToHcpModalSelector} ${linkToHcpModalCancelButtonSelector}`);
|
|
||||||
assert.dom(linkToHcpModalSelector).doesNotExist('Link to HCP modal is gone after cancel');
|
|
||||||
|
|
||||||
// Click on the link to HCP nav item
|
|
||||||
await click(`${linkToHcpSelector} button`);
|
|
||||||
|
|
||||||
// link to HCP modal appears
|
|
||||||
assert.dom(linkToHcpModalSelector).isVisible('Link to HCP modal is visible');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -8,228 +8,69 @@ import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { render } from '@ember/test-helpers';
|
import { render } from '@ember/test-helpers';
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
import { EnvStub } from 'consul-ui/services/env';
|
import { EnvStub } from 'consul-ui/services/env';
|
||||||
import Service from '@ember/service';
|
|
||||||
|
|
||||||
const backToHcpSelector = '[data-test-back-to-hcp]';
|
|
||||||
const hcpConsulCentralItemSelector = '[data-test-linked-cluster-hcp-link]';
|
|
||||||
const linkToHcpSelector = '[data-test-link-to-hcp]';
|
|
||||||
const resourceId =
|
|
||||||
'organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api';
|
|
||||||
module('Integration | Component | hcp nav item', function (hooks) {
|
module('Integration | Component | hcp nav item', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
module('back to hcp item', function () {
|
test('it prints the value of CONSUL_HCP_URL', async function (assert) {
|
||||||
test('it prints the value of CONSUL_HCP_URL when env vars are set', async function (assert) {
|
this.owner.register(
|
||||||
this.owner.register(
|
'service:env',
|
||||||
'service:env',
|
class Stub extends EnvStub {
|
||||||
class Stub extends EnvStub {
|
stubEnv = {
|
||||||
stubEnv = {
|
CONSUL_HCP_URL: 'http://hcp.com',
|
||||||
CONSUL_HCP_URL: 'http://hcp.com',
|
CONSUL_HCP_ENABLED: true,
|
||||||
CONSUL_HCP_ENABLED: true,
|
};
|
||||||
};
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<Hds::SideNav::List as |SNL|>
|
<Hds::SideNav::List as |SNL|>
|
||||||
<HcpNavItem @list={{SNL}} />
|
<HcpNavItem @list={{SNL}} />
|
||||||
</Hds::SideNav::List>
|
</Hds::SideNav::List>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.dom(backToHcpSelector).isVisible();
|
assert.dom('[data-test-back-to-hcp]').isVisible();
|
||||||
assert.dom('a').hasAttribute('href', 'http://hcp.com');
|
assert.dom('a').hasAttribute('href', 'http://hcp.com');
|
||||||
assert.dom(linkToHcpSelector).doesNotExist('link to hcp should not be visible');
|
|
||||||
assert
|
|
||||||
.dom(hcpConsulCentralItemSelector)
|
|
||||||
.doesNotExist('hcp consul central item should not be visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it does not output the Back to HCP link if CONSUL_HCP_URL is not present', async function (assert) {
|
|
||||||
this.owner.register(
|
|
||||||
'service:env',
|
|
||||||
class Stub extends EnvStub {
|
|
||||||
stubEnv = {
|
|
||||||
CONSUL_HCP_ENABLED: true,
|
|
||||||
CONSUL_HCP_URL: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`
|
|
||||||
<Hds::SideNav::List as |SNL|>
|
|
||||||
<HcpNavItem @list={{SNL}} />
|
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist();
|
|
||||||
assert.dom('a').doesNotExist();
|
|
||||||
});
|
|
||||||
test('it does not output the Back to HCP link if CONSUL_HCP_ENABLED is not present', async function (assert) {
|
|
||||||
this.owner.register(
|
|
||||||
'service:env',
|
|
||||||
class Stub extends EnvStub {
|
|
||||||
stubEnv = {
|
|
||||||
CONSUL_HCP_URL: 'http://hcp.com',
|
|
||||||
CONSUL_HCP_ENABLED: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`
|
|
||||||
<Hds::SideNav::List as |SNL|>
|
|
||||||
<HcpNavItem @list={{SNL}} />
|
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist();
|
|
||||||
assert.dom('a').doesNotExist();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.skip('when rendered in self managed mode', function (hooks) {
|
test('it does not output the Back to HCP link if CONSUL_HCP_URL is not present', async function (assert) {
|
||||||
hooks.beforeEach(function () {
|
this.owner.register(
|
||||||
this.owner.register(
|
'service:env',
|
||||||
'service:env',
|
class Stub extends EnvStub {
|
||||||
class Stub extends EnvStub {
|
stubEnv = {
|
||||||
stubEnv = {};
|
CONSUL_HCP_ENABLED: true,
|
||||||
}
|
CONSUL_HCP_URL: undefined,
|
||||||
);
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
test('when unauthorized to link it does not display any nav items', async function (assert) {
|
await render(hbs`
|
||||||
this.owner.register(
|
<Hds::SideNav::List as |SNL|>
|
||||||
'service:hcp-link-status',
|
<HcpNavItem @list={{SNL}} />
|
||||||
class Stub extends Service {
|
</Hds::SideNav::List>
|
||||||
hasPermissionToLink = false;
|
`);
|
||||||
}
|
|
||||||
);
|
|
||||||
this.linkData = {
|
|
||||||
resourceId,
|
|
||||||
isLinked: false,
|
|
||||||
};
|
|
||||||
await render(hbs`
|
|
||||||
<Hds::SideNav::List as |SNL|>
|
|
||||||
<HcpNavItem @list={{SNL}} @linkData={{this.linkData}}/>
|
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist('back to hcp should not be visible');
|
|
||||||
assert.dom(linkToHcpSelector).doesNotExist('link to hcp should not be visible');
|
|
||||||
assert
|
|
||||||
.dom(hcpConsulCentralItemSelector)
|
|
||||||
.doesNotExist('hcp consul central item should not be visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('when link status is undefined it does not display any nav items', async function (assert) {
|
assert.dom('[data-test-back-to-hcp]').doesNotExist();
|
||||||
this.owner.register(
|
assert.dom('a').doesNotExist();
|
||||||
'service:hcp-link-status',
|
});
|
||||||
class Stub extends Service {
|
test('it does not output the Back to HCP link if CONSUL_HCP_ENABLED is not present', async function (assert) {
|
||||||
hasPermissionToLink = true;
|
this.owner.register(
|
||||||
}
|
'service:env',
|
||||||
);
|
class Stub extends EnvStub {
|
||||||
this.linkData = {
|
stubEnv = {
|
||||||
resourceId,
|
CONSUL_HCP_URL: 'http://hcp.com',
|
||||||
};
|
CONSUL_HCP_ENABLED: undefined,
|
||||||
await render(hbs`
|
};
|
||||||
<Hds::SideNav::List as |SNL|>
|
}
|
||||||
<HcpNavItem @list={{SNL}} @linkData={{this.linkData}}/>
|
);
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist('back to hcp should not be visible');
|
|
||||||
assert.dom(linkToHcpSelector).doesNotExist('link to hcp should not be visible');
|
|
||||||
assert
|
|
||||||
.dom(hcpConsulCentralItemSelector)
|
|
||||||
.doesNotExist('hcp consul central item should not be visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('when already linked but no resourceId it does not display any nav items', async function (assert) {
|
await render(hbs`
|
||||||
this.owner.register(
|
<Hds::SideNav::List as |SNL|>
|
||||||
'service:hcp-link-status',
|
<HcpNavItem @list={{SNL}} />
|
||||||
class Stub extends Service {
|
</Hds::SideNav::List>
|
||||||
hasPermissionToLink = true;
|
`);
|
||||||
}
|
|
||||||
);
|
|
||||||
this.linkData = {
|
|
||||||
isLinked: true,
|
|
||||||
};
|
|
||||||
await render(hbs`
|
|
||||||
<Hds::SideNav::List as |SNL|>
|
|
||||||
<HcpNavItem @list={{SNL}} @linkData={{this.linkData}}/>
|
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist('back to hcp should not be visible');
|
|
||||||
assert.dom(linkToHcpSelector).doesNotExist('link to hcp should not be visible');
|
|
||||||
assert
|
|
||||||
.dom(hcpConsulCentralItemSelector)
|
|
||||||
.doesNotExist('hcp consul central item should not be visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('when already linked and we have a resourceId it displays the link to hcp consul central item', async function (assert) {
|
assert.dom('[data-test-back-to-hcp]').doesNotExist();
|
||||||
this.owner.register(
|
assert.dom('a').doesNotExist();
|
||||||
'service:hcp-link-status',
|
|
||||||
class Stub extends Service {
|
|
||||||
hasPermissionToLink = true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.linkData = {
|
|
||||||
isLinked: true,
|
|
||||||
resourceId,
|
|
||||||
};
|
|
||||||
await render(hbs`
|
|
||||||
<Hds::SideNav::List as |SNL|>
|
|
||||||
<HcpNavItem @list={{SNL}} @linkData={{this.linkData}}/>
|
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist('back to hcp should not be visible');
|
|
||||||
assert.dom(linkToHcpSelector).doesNotExist('link to hcp should not be visible');
|
|
||||||
assert
|
|
||||||
.dom(hcpConsulCentralItemSelector)
|
|
||||||
.isVisible('hcp consul central item should be visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('when not already linked without dismissed banner it displays the link to hcp item', async function (assert) {
|
|
||||||
this.owner.register(
|
|
||||||
'service:hcp-link-status',
|
|
||||||
class Stub extends Service {
|
|
||||||
hasPermissionToLink = true;
|
|
||||||
shouldDisplayBanner = true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.linkData = {
|
|
||||||
isLinked: false,
|
|
||||||
};
|
|
||||||
await render(hbs`
|
|
||||||
<Hds::SideNav::List as |SNL|>
|
|
||||||
<HcpNavItem @list={{SNL}} @linkData={{this.linkData}}/>
|
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist('back to hcp should not be visible');
|
|
||||||
assert
|
|
||||||
.dom(hcpConsulCentralItemSelector)
|
|
||||||
.doesNotExist('hcp consul central item should not be visible');
|
|
||||||
assert.dom(linkToHcpSelector).isVisible('link to hcp should be visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('when not already linked with dismissed banner it displays the link to hcp item', async function (assert) {
|
|
||||||
this.owner.register(
|
|
||||||
'service:hcp-link-status',
|
|
||||||
class Stub extends Service {
|
|
||||||
hasPermissionToLink = true;
|
|
||||||
shouldDisplayBanner = false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.linkData = {
|
|
||||||
isLinked: false,
|
|
||||||
};
|
|
||||||
await render(hbs`
|
|
||||||
<Hds::SideNav::List as |SNL|>
|
|
||||||
<HcpNavItem @list={{SNL}} @linkData={{this.linkData}}/>
|
|
||||||
</Hds::SideNav::List>
|
|
||||||
`);
|
|
||||||
assert.dom(backToHcpSelector).doesNotExist('back to hcp should not be visible');
|
|
||||||
assert
|
|
||||||
.dom(hcpConsulCentralItemSelector)
|
|
||||||
.doesNotExist('hcp consul central item should not be visible');
|
|
||||||
assert.dom(linkToHcpSelector).isVisible('link to hcp should be visible');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { module, test } from 'qunit';
|
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
|
||||||
import { click, render } from '@ember/test-helpers';
|
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
|
||||||
import Service from '@ember/service';
|
|
||||||
import sinon from 'sinon';
|
|
||||||
import { EnvStub } from 'consul-ui/services/env';
|
|
||||||
|
|
||||||
const userDismissedBannerStub = sinon.stub();
|
|
||||||
const dismissHcpLinkBannerStub = sinon.stub();
|
|
||||||
const bannerSelector = '[data-test-link-to-hcp-banner]';
|
|
||||||
module('Integration | Component | link-to-hcp-banner', function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
class HcpLinkStatusStub extends Service {
|
|
||||||
get shouldDisplayBanner() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
userDismissedBanner = userDismissedBannerStub;
|
|
||||||
dismissHcpLinkBanner = dismissHcpLinkBannerStub;
|
|
||||||
}
|
|
||||||
|
|
||||||
hooks.beforeEach(function () {
|
|
||||||
this.owner.register('service:hcp-link-status', HcpLinkStatusStub);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it renders banner when hcp-link-status says it should', async function (assert) {
|
|
||||||
this.linkData = { isLinked: false };
|
|
||||||
await render(hbs`<LinkToHcpBanner @linkData={{this.linkData}} />`);
|
|
||||||
|
|
||||||
assert.dom(bannerSelector).exists({ count: 1 });
|
|
||||||
await click(`${bannerSelector} button[aria-label="Dismiss"]`);
|
|
||||||
assert.ok(dismissHcpLinkBannerStub.calledOnce, 'userDismissedBanner was called');
|
|
||||||
// Can't test that banner is no longer visible since service isn't hooked up
|
|
||||||
assert
|
|
||||||
.dom('[data-test-link-to-hcp-banner-title]')
|
|
||||||
.hasText(
|
|
||||||
'Link this cluster to HCP Consul Central in a few steps to start managing your clusters in one place'
|
|
||||||
);
|
|
||||||
assert
|
|
||||||
.dom('[data-test-link-to-hcp-banner-description]')
|
|
||||||
.hasText(
|
|
||||||
'By linking your clusters to HCP Consul Central, you’ll get global, cross-cluster metrics, visual service maps, and a global API. Link to access a free 90 day trial for full feature access in your HCP organization.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('banner does not render when hcp-link-status says it should NOT', async function (assert) {
|
|
||||||
class HcpLinkStatusStub extends Service {
|
|
||||||
get shouldDisplayBanner() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dismissHcpLinkBanner = sinon.stub();
|
|
||||||
}
|
|
||||||
this.owner.register('service:hcp-link-status', HcpLinkStatusStub);
|
|
||||||
this.linkData = { isLinked: false };
|
|
||||||
await render(hbs`<LinkToHcpBanner @linkData={{this.linkData}} />`);
|
|
||||||
assert.dom(bannerSelector).doesNotExist();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('banner does not render when cluster is already linked', async function (assert) {
|
|
||||||
class HcpLinkStatusStub extends Service {
|
|
||||||
get shouldDisplayBanner() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
dismissHcpLinkBanner = sinon.stub();
|
|
||||||
}
|
|
||||||
this.owner.register('service:hcp-link-status', HcpLinkStatusStub);
|
|
||||||
this.linkData = { isLinked: true };
|
|
||||||
await render(hbs`<LinkToHcpBanner @linkData={{this.linkData}} />`);
|
|
||||||
assert.dom(bannerSelector).doesNotExist();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('banner does not render when we have no cluster link status info', async function (assert) {
|
|
||||||
class HcpLinkStatusStub extends Service {
|
|
||||||
get shouldDisplayBanner() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
dismissHcpLinkBanner = sinon.stub();
|
|
||||||
}
|
|
||||||
this.owner.register('service:hcp-link-status', HcpLinkStatusStub);
|
|
||||||
this.linkData = undefined;
|
|
||||||
await render(hbs`<LinkToHcpBanner @linkData={{this.linkData}} />`);
|
|
||||||
assert.dom(bannerSelector).doesNotExist();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it displays different banner text when consul is enterprise', async function (assert) {
|
|
||||||
this.owner.register(
|
|
||||||
'service:env',
|
|
||||||
class Stub extends EnvStub {
|
|
||||||
stubEnv = {};
|
|
||||||
isEnterprise = true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.linkData = { isLinked: false };
|
|
||||||
|
|
||||||
await render(hbs`<LinkToHcpBanner @linkData={{this.linkData}} />`);
|
|
||||||
assert
|
|
||||||
.dom('[data-test-link-to-hcp-banner-description]')
|
|
||||||
.hasText(
|
|
||||||
'By linking your clusters to HCP Consul Central, you’ll get global, cross-cluster metrics, visual service maps, and a global API. HCP Consul Central’s full feature set is included with an Enterprise license.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,230 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { module, test } from 'qunit';
|
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
|
||||||
import { click, render } from '@ember/test-helpers';
|
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
|
||||||
import Service, { inject as service } from '@ember/service';
|
|
||||||
import DataSourceComponent from 'consul-ui/components/data-source/index';
|
|
||||||
import sinon from 'sinon';
|
|
||||||
import { BlockingEventSource as RealEventSource } from 'consul-ui/utils/dom/event-source';
|
|
||||||
import { ACCESS_LEVEL } from 'consul-ui/components/link-to-hcp-modal';
|
|
||||||
|
|
||||||
const modalSelector = '[data-test-link-to-hcp-modal]';
|
|
||||||
const modalRefreshPageAlertSelector = '[data-test-link-to-hcp-modal-refresh-page-alert]';
|
|
||||||
const modalNoACLsAlertSelector = '[data-test-link-to-hcp-modal-no-acls-alert]';
|
|
||||||
const modalOptionReadOnlySelector = '#accessMode-readonly';
|
|
||||||
const modalOptionReadOnlyErrorSelector = '[data-test-link-to-hcp-modal-access-level-options-error]';
|
|
||||||
const modalGenerateTokenCardSelector = '[data-test-link-to-hcp-modal-generate-token-card]';
|
|
||||||
const modalGenerateTokenCardValueSelector =
|
|
||||||
'[data-test-link-to-hcp-modal-generate-token-card-value]';
|
|
||||||
const modalGenerateTokenCardCopyButtonSelector =
|
|
||||||
'[data-test-link-to-hcp-modal-generate-token-card-copy-button]';
|
|
||||||
const modalGenerateTokenButtonSelector = '[data-test-link-to-hcp-modal-generate-token-button]';
|
|
||||||
const modalGenerateTokenMissedPolicyAlertSelector =
|
|
||||||
'[data-test-link-to-hcp-modal-missed-policy-alert]';
|
|
||||||
const modalNextButtonSelector = '[data-test-link-to-hcp-modal-next-button]';
|
|
||||||
const modalCancelButtonSelector = '[data-test-link-to-hcp-modal-cancel-button]';
|
|
||||||
|
|
||||||
module('Integration | Component | link-to-hcp-modal', function (hooks) {
|
|
||||||
let originalClipboardWriteText;
|
|
||||||
let hideModal = sinon.stub();
|
|
||||||
const close = sinon.stub();
|
|
||||||
const source = new RealEventSource();
|
|
||||||
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
hooks.beforeEach(function () {
|
|
||||||
const fakeService = class extends Service {
|
|
||||||
close = close;
|
|
||||||
open() {
|
|
||||||
source.getCurrentEvent = function () {
|
|
||||||
return { data: { Name: 'global-read-only', ID: '00000000-0000-0000-0000-000000000002' } };
|
|
||||||
};
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.owner.register('service:data-source/fake-service', fakeService);
|
|
||||||
this.owner.register(
|
|
||||||
'component:data-source',
|
|
||||||
class extends DataSourceComponent {
|
|
||||||
@service('data-source/fake-service') dataSource;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.owner.register(
|
|
||||||
'service:abilities',
|
|
||||||
class Stub extends Service {
|
|
||||||
can(permission) {
|
|
||||||
if (permission === 'create tokens') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (permission === 'read acls') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.owner.register(
|
|
||||||
'service:hcp-link-modal',
|
|
||||||
class Stub extends Service {
|
|
||||||
hide = hideModal;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
originalClipboardWriteText = navigator.clipboard.writeText;
|
|
||||||
navigator.clipboard.writeText = sinon.stub();
|
|
||||||
});
|
|
||||||
|
|
||||||
hooks.afterEach(function () {
|
|
||||||
navigator.clipboard.writeText = originalClipboardWriteText;
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it renders modal', async function (assert) {
|
|
||||||
await render(hbs`<LinkToHcpModal @dc="dc-1"
|
|
||||||
@nspace="default"
|
|
||||||
@partition="-" />`);
|
|
||||||
|
|
||||||
assert.dom(modalSelector).exists({ count: 1 });
|
|
||||||
assert.dom(`${modalSelector} ${modalNoACLsAlertSelector}`).doesNotExist();
|
|
||||||
assert.dom(`${modalSelector} ${modalRefreshPageAlertSelector}`).isVisible();
|
|
||||||
|
|
||||||
// select read-only
|
|
||||||
await click(`${modalSelector} ${modalOptionReadOnlySelector}`);
|
|
||||||
|
|
||||||
// when read-only selected, it shows the generate token button
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenButtonSelector}`).isVisible();
|
|
||||||
|
|
||||||
// with the correct policy, it doesn't show the missed policy alert
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenMissedPolicyAlertSelector}`).doesNotExist();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it updates next link on option selected', async function (assert) {
|
|
||||||
await render(hbs`<LinkToHcpModal @dc="dc-1"
|
|
||||||
@nspace="default"
|
|
||||||
@partition="-" />`);
|
|
||||||
|
|
||||||
let hrefValue = this.element
|
|
||||||
.querySelector(`${modalSelector} ${modalNextButtonSelector}`)
|
|
||||||
.getAttribute('href');
|
|
||||||
assert.ok(
|
|
||||||
hrefValue.includes(ACCESS_LEVEL.GLOBALREADWRITE),
|
|
||||||
'next link includes read/write access level'
|
|
||||||
);
|
|
||||||
|
|
||||||
// select read-only
|
|
||||||
await click(`${modalSelector} ${modalOptionReadOnlySelector}`);
|
|
||||||
|
|
||||||
hrefValue = this.element
|
|
||||||
.querySelector(`${modalSelector} ${modalNextButtonSelector}`)
|
|
||||||
.getAttribute('href');
|
|
||||||
assert.ok(
|
|
||||||
hrefValue.includes(ACCESS_LEVEL.GLOBALREADONLY),
|
|
||||||
'next link includes read-only access level'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it creates token and copy it to clipboard', async function (assert) {
|
|
||||||
await render(hbs`<LinkToHcpModal @dc="dc-1"
|
|
||||||
@nspace="default"
|
|
||||||
@partition="-" />`);
|
|
||||||
// select read-only
|
|
||||||
await click(`${modalSelector} ${modalOptionReadOnlySelector}`);
|
|
||||||
assert
|
|
||||||
.dom(`${modalSelector} ${modalGenerateTokenButtonSelector}`)
|
|
||||||
.hasText('Generate a read-only ACL token');
|
|
||||||
|
|
||||||
// with the correct policy, it doesn't show the missed policy alert
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenMissedPolicyAlertSelector}`).doesNotExist();
|
|
||||||
|
|
||||||
// trigger generate token
|
|
||||||
await click(`${modalSelector} ${modalGenerateTokenButtonSelector}`);
|
|
||||||
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenCardSelector}`).isVisible();
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenCardValueSelector}`).exists();
|
|
||||||
const tokenValue = this.element.querySelector(
|
|
||||||
`${modalSelector} ${modalGenerateTokenCardValueSelector}`
|
|
||||||
).textContent;
|
|
||||||
// click on copy button
|
|
||||||
await click(`${modalSelector} ${modalGenerateTokenCardCopyButtonSelector}`);
|
|
||||||
assert.ok(
|
|
||||||
navigator.clipboard.writeText.called,
|
|
||||||
'clipboard write function is called when copy button is clicked'
|
|
||||||
);
|
|
||||||
assert.ok(
|
|
||||||
navigator.clipboard.writeText.calledWith(tokenValue.trim()),
|
|
||||||
'clipboard contains expected value'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it calls hcpLinkModal.hide when closing modal', async function (assert) {
|
|
||||||
await render(hbs`<LinkToHcpModal @dc="dc-1"
|
|
||||||
@nspace="default"
|
|
||||||
@partition="-" />`);
|
|
||||||
|
|
||||||
await click(`${modalSelector} ${modalCancelButtonSelector}`);
|
|
||||||
|
|
||||||
assert.ok(hideModal.called, 'hide method is called when cancel button is clicked');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it shows an alert when policy was not loaded and it is not possible to generate a token', async function (assert) {
|
|
||||||
// creating a fake service that will return an empty policy
|
|
||||||
const fakeService = class extends Service {
|
|
||||||
close = close;
|
|
||||||
open() {
|
|
||||||
source.getCurrentEvent = function () {
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.owner.register('service:data-source/fake-service', fakeService);
|
|
||||||
|
|
||||||
await render(hbs`<LinkToHcpModal @dc="dc-1"
|
|
||||||
@nspace="default"
|
|
||||||
@partition="-" />`);
|
|
||||||
|
|
||||||
assert.dom(modalSelector).exists({ count: 1 });
|
|
||||||
assert.dom(`${modalSelector} ${modalNoACLsAlertSelector}`).doesNotExist();
|
|
||||||
assert.dom(`${modalSelector} ${modalRefreshPageAlertSelector}`).isVisible();
|
|
||||||
|
|
||||||
// select read-only
|
|
||||||
await click(`${modalSelector} ${modalOptionReadOnlySelector}`);
|
|
||||||
|
|
||||||
// when read-only selected and no policy, it doesn't show the generate token button
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenButtonSelector}`).doesNotExist();
|
|
||||||
// Missed policy alert is visible
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenMissedPolicyAlertSelector}`).isVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it shows an error wher read-only selected and acls are disabled', async function (assert) {
|
|
||||||
this.owner.register(
|
|
||||||
'service:abilities',
|
|
||||||
class Stub extends Service {
|
|
||||||
can(permission) {
|
|
||||||
if (permission === 'read acls') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(hbs`<LinkToHcpModal @dc="dc-1"
|
|
||||||
@nspace="default"
|
|
||||||
@partition="-" />`);
|
|
||||||
|
|
||||||
assert.dom(modalSelector).exists({ count: 1 });
|
|
||||||
assert.dom(`${modalSelector} ${modalNoACLsAlertSelector}`).isVisible();
|
|
||||||
assert.dom(`${modalSelector} ${modalRefreshPageAlertSelector}`).isVisible();
|
|
||||||
|
|
||||||
// select read-only
|
|
||||||
await click(`${modalSelector} ${modalOptionReadOnlySelector}`);
|
|
||||||
|
|
||||||
// when read-only selected and no policy, it doesn't show the generate token button
|
|
||||||
assert.dom(`${modalSelector} ${modalGenerateTokenButtonSelector}`).doesNotExist();
|
|
||||||
// No acls enabled error is presented
|
|
||||||
assert.dom(`${modalSelector} ${modalOptionReadOnlyErrorSelector}`).isVisible();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,60 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { module, test } from 'qunit';
|
|
||||||
import { render } from '@ember/test-helpers';
|
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
|
||||||
import { HCP_PREFIX } from 'consul-ui/helpers/hcp-authentication-link';
|
|
||||||
import { EnvStub } from 'consul-ui/services/env';
|
|
||||||
|
|
||||||
const clusterName = 'hello';
|
|
||||||
const clusterVersion = '1.18.0';
|
|
||||||
const accessMode = 'CONSUL_ACCESS_LEVEL_GLOBAL_READ_WRITE';
|
|
||||||
module('Integration | Helper | hcp-authentication-link', function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
hooks.beforeEach(function () {
|
|
||||||
this.owner.register(
|
|
||||||
'service:env',
|
|
||||||
class Stub extends EnvStub {
|
|
||||||
stubEnv = {
|
|
||||||
CONSUL_VERSION: clusterVersion,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('it makes a URL out of a real resourceId', async function (assert) {
|
|
||||||
this.dcName = clusterName;
|
|
||||||
|
|
||||||
await render(hbs`{{hcp-authentication-link dcName}}`);
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
this.element.textContent.trim(),
|
|
||||||
`${HCP_PREFIX}?cluster_name=${clusterName}&cluster_version=${clusterVersion}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it returns correct link without dc name', async function (assert) {
|
|
||||||
this.dcName = null;
|
|
||||||
|
|
||||||
await render(hbs`{{hcp-authentication-link dcName}}`);
|
|
||||||
assert.equal(
|
|
||||||
this.element.textContent.trim(),
|
|
||||||
`${HCP_PREFIX}?cluster_version=${clusterVersion}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it makes a URL out of a dc name and accessLevel, if passed', async function (assert) {
|
|
||||||
this.dcName = clusterName;
|
|
||||||
this.accessMode = accessMode;
|
|
||||||
|
|
||||||
await render(hbs`{{hcp-authentication-link dcName accessMode}}`);
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
this.element.textContent.trim(),
|
|
||||||
`${HCP_PREFIX}?cluster_name=${clusterName}&cluster_version=${clusterVersion}&cluster_access_mode=${accessMode}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,41 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) HashiCorp, Inc.
|
|
||||||
* SPDX-License-Identifier: BUSL-1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { module, test } from 'qunit';
|
|
||||||
import { render } from '@ember/test-helpers';
|
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
|
||||||
import { HCP_PREFIX } from 'consul-ui/helpers/hcp-resource-id-to-link';
|
|
||||||
|
|
||||||
// organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api
|
|
||||||
const clusterName = 'hello';
|
|
||||||
const projectId = '4b09958c-fa91-43ab-8029-eb28d8cee9d4';
|
|
||||||
const realResourceId = `organization/b4432207-bb9c-438e-a160-b98923efa979/project/${projectId}/hashicorp.consul.global-network-manager.cluster/${clusterName}`;
|
|
||||||
module('Integration | Helper | hcp-resource-id-to-link', function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
test('it makes a URL out of a real resourceId', async function (assert) {
|
|
||||||
this.resourceId = realResourceId;
|
|
||||||
|
|
||||||
await render(hbs`{{hcp-resource-id-to-link resourceId}}`);
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
this.element.textContent.trim(),
|
|
||||||
`${HCP_PREFIX}/${clusterName}?project_id=${projectId}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it returns empty string with invalid resourceId', async function (assert) {
|
|
||||||
this.resourceId = 'invalid';
|
|
||||||
|
|
||||||
await render(hbs`{{hcp-resource-id-to-link resourceId}}`);
|
|
||||||
assert.equal(this.element.textContent.trim(), '');
|
|
||||||
|
|
||||||
// not enough items in id
|
|
||||||
this.resourceId =
|
|
||||||
'`organization/b4432207-bb9c-438e-a160-b98923efa979/project/${projectId}/hashicorp.consul.global-network-manager.cluster`';
|
|
||||||
await render(hbs`{{hcp-resource-id-to-link resourceId}}`);
|
|
||||||
assert.equal(this.element.textContent.trim(), '');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -10,8 +10,9 @@ import { setupTest } from 'ember-qunit';
|
||||||
module('Unit | Ability | *', function (hooks) {
|
module('Unit | Ability | *', function (hooks) {
|
||||||
setupTest(hooks);
|
setupTest(hooks);
|
||||||
|
|
||||||
|
// Replace this with your real tests.
|
||||||
test('it exists', function (assert) {
|
test('it exists', function (assert) {
|
||||||
assert.expect(240);
|
assert.expect(228);
|
||||||
|
|
||||||
const abilities = Object.keys(requirejs.entries)
|
const abilities = Object.keys(requirejs.entries)
|
||||||
.filter((key) => key.indexOf('/abilities/') !== -1)
|
.filter((key) => key.indexOf('/abilities/') !== -1)
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
# Copyright (c) HashiCorp, Inc.
|
|
||||||
# SPDX-License-Identifier: BUSL-1.1
|
|
||||||
|
|
||||||
title: Link this cluster to HCP Consul Central in a few steps to start managing your clusters in one place
|
|
||||||
description: By linking your clusters to HCP Consul Central, you’ll get global, cross-cluster metrics, visual service maps, and a global API. {isEnterprise, select,
|
|
||||||
true {HCP Consul Central’s full feature set is included with an Enterprise license.}
|
|
||||||
other {Link to access a free 90 day trial for full feature access in your HCP organization.}}
|
|
||||||
clusterLinkButton: Link this cluster
|
|
||||||
viewDocumentation: View documentation
|
|
||||||
consulCentralDocumentation: Consul Central documentation
|
|
Loading…
Reference in New Issue