Refactor edition enablement (#1657)
* refactor: 💡 consolidate ssh features into ssh-target flag ✅ Closes: ICU-8383 * refactor: 💡 factor out base edition * feat: 🎸 dynamic feature edition enablement and toggling
This commit is contained in:
1 parent
bd6eee8eb3
commit
acd719140d
24 files changed
+303
-91
No files matched your search
@@ -45,3 +45,5 @@ learn-more: Learn more
|
|||||||
refresh: Refresh
|
refresh: Refresh
|
||||||
remove-resources: Remove resources and save
|
remove-resources: Remove resources and save
|
||||||
do-this-later: Do this later
|
do-this-later: Do this later
|
||||||
|
toggle-color-theme: Toggle Color Theme
|
||||||
|
toggle-feature-edition: Toggle Feature Edition
|
||||||
@@ -28,7 +28,6 @@ titles:
|
|||||||
actions: Actions
|
actions: Actions
|
||||||
documentation: Documentation
|
documentation: Documentation
|
||||||
abandon-confirm: Discard unsaved changes?
|
abandon-confirm: Discard unsaved changes?
|
||||||
toggle-color-theme: Toggle Color Theme
|
|
||||||
welcome-to-boundary: Welcome to Boundary
|
welcome-to-boundary: Welcome to Boundary
|
||||||
label: Label
|
label: Label
|
||||||
value: Value
|
value: Value
|
||||||
@@ -38,6 +37,12 @@ titles:
|
|||||||
success: Success
|
success: Success
|
||||||
create-resources: Create Resources
|
create-resources: Create Resources
|
||||||
choose-an-option: Choose an option
|
choose-an-option: Choose an option
|
||||||
|
feature-edition: 'Edition: {edition}'
|
||||||
|
editions:
|
||||||
|
oss: OSS
|
||||||
|
ent: Enterprise
|
||||||
|
enterprise: Enterprise
|
||||||
|
hcp: HCP
|
||||||
descriptions:
|
descriptions:
|
||||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||||
cluster-url-initialization: To get started, please enter your cluster URL
|
cluster-url-initialization: To get started, please enter your cluster URL
|
||||||
|
|||||||
@@ -12,13 +12,12 @@ export default class OverrideCredentialLibraryAbility extends CredentialLibraryA
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This override ensures that vault ssh cert may be read only if the
|
* This override ensures that vault ssh cert may be read only if the
|
||||||
* credential-library-vault-ssh-certificate feature flag is enabled.
|
* ssh-target feature flag is enabled.
|
||||||
* All other types are subject to the standard logic found in the api addon.
|
* All other types are subject to the standard logic found in the api addon.
|
||||||
*/
|
*/
|
||||||
get canRead() {
|
get canRead() {
|
||||||
return this.features.isEnabled(
|
return this.features.isEnabled('ssh-target') ||
|
||||||
'credential-library-vault-ssh-certificate'
|
!this.model.isVaultSSHCertificate
|
||||||
) || !this.model.isVaultSSHCertificate
|
|
||||||
? super.canRead
|
? super.canRead
|
||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</Hds::Form::Textarea::Field>
|
</Hds::Form::Textarea::Field>
|
||||||
|
|
||||||
{{#if
|
{{#if (and @model.isNew (feature-flag 'ssh-target'))}}
|
||||||
(and @model.isNew (feature-flag 'credential-library-vault-ssh-certificate'))
|
|
||||||
}}
|
|
||||||
<Hds::Form::RadioCard::Group @name='type' @alignment='center' as |G|>
|
<Hds::Form::RadioCard::Group @name='type' @alignment='center' as |G|>
|
||||||
<G.Legend>{{t 'form.type.label'}}</G.Legend>
|
<G.Legend>{{t 'form.type.label'}}</G.Legend>
|
||||||
{{#each this.types as |type|}}
|
{{#each this.types as |type|}}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class='target-worker-filter' data-test-filter={{@name}}>
|
<div class='target-worker-filter' data-test-filter={{@name}}>
|
||||||
|
|
||||||
<Hds::Form::Toggle::Field
|
<Hds::Form::Toggle::Field
|
||||||
name='target-worker-filter-toggle'
|
name={{concat 'target-worker-filter-toggle-' @name}}
|
||||||
checked={{@toggleEnabled}}
|
checked={{@toggleEnabled}}
|
||||||
disabled={{@disabled}}
|
disabled={{@disabled}}
|
||||||
@isOptional={{true}}
|
@isOptional={{true}}
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ export default class ApplicationController extends Controller {
|
|||||||
|
|
||||||
@service session;
|
@service session;
|
||||||
@service flashMessages;
|
@service flashMessages;
|
||||||
|
@service featureEdition;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export function initialize(owner) {
|
||||||
|
const service = owner.lookup('service:feature-edition');
|
||||||
|
service.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
initialize,
|
||||||
|
};
|
||||||
@@ -20,6 +20,7 @@ export default class ApplicationRoute extends Route {
|
|||||||
@service confirm;
|
@service confirm;
|
||||||
@service router;
|
@service router;
|
||||||
@service intl;
|
@service intl;
|
||||||
|
@service featureEdition;
|
||||||
|
|
||||||
// =attributes
|
// =attributes
|
||||||
|
|
||||||
@@ -126,4 +127,9 @@ export default class ApplicationRoute extends Route {
|
|||||||
rootEl.classList.remove('rose-theme-light');
|
rootEl.classList.remove('rose-theme-light');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleEdition(edition) {
|
||||||
|
this.featureEdition.setEdition(edition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -36,7 +36,7 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari
|
|||||||
|
|
||||||
// Set the type to generic vault if feature flag isn't enabled in cases where
|
// Set the type to generic vault if feature flag isn't enabled in cases where
|
||||||
// user sets the query parameter manually
|
// user sets the query parameter manually
|
||||||
if (!this.features.isEnabled('credential-library-vault-ssh-certificate')) {
|
if (!this.features.isEnabled('ssh-target')) {
|
||||||
type = TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC;
|
type = TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import Service from '@ember/service';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
import config from 'admin/config/environment';
|
||||||
|
|
||||||
|
const { selectedEdition, featureEditions } = config;
|
||||||
|
|
||||||
|
export default class FeatureEditionService extends Service {
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service features;
|
||||||
|
|
||||||
|
// =attributes
|
||||||
|
|
||||||
|
@tracked edition;
|
||||||
|
|
||||||
|
// array of edition strings
|
||||||
|
get editions() {
|
||||||
|
return Object.keys(featureEditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the selected edition from the config.
|
||||||
|
*/
|
||||||
|
initialize() {
|
||||||
|
this.setEdition(selectedEdition);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEdition(edition) {
|
||||||
|
const editionFlags = featureEditions[edition];
|
||||||
|
Object.keys(editionFlags).forEach((flag) => {
|
||||||
|
if (editionFlags[flag]) {
|
||||||
|
this.features.enable(flag);
|
||||||
|
} else {
|
||||||
|
this.features.disable(flag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.edition = edition;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<dropdown.separator />
|
<dropdown.separator />
|
||||||
|
|
||||||
<dropdown.section @title={{t 'titles.toggle-color-theme'}}>
|
<dropdown.section @title={{t 'actions.toggle-color-theme'}}>
|
||||||
<Rose::Form as |form|>
|
<Rose::Form as |form|>
|
||||||
<form.radioGroup
|
<form.radioGroup
|
||||||
@name='theme'
|
@name='theme'
|
||||||
@@ -67,6 +67,39 @@
|
|||||||
</dropdown.section>
|
</dropdown.section>
|
||||||
|
|
||||||
</utilities.dropdown>
|
</utilities.dropdown>
|
||||||
|
|
||||||
|
{{#if (feature-flag 'dev-edition-toggle')}}
|
||||||
|
<utilities.dropdown
|
||||||
|
data-test-dev-edition-toggle
|
||||||
|
@text={{t
|
||||||
|
'titles.feature-edition'
|
||||||
|
edition=(t (concat 'editions.' this.featureEdition.edition))
|
||||||
|
}}
|
||||||
|
@icon='flight-icons/svg/layers-16'
|
||||||
|
as |dropdown|
|
||||||
|
>
|
||||||
|
<dropdown.section @title={{t 'actions.toggle-feature-edition'}}>
|
||||||
|
<Rose::Form as |form|>
|
||||||
|
<form.radioGroup
|
||||||
|
@name='edition'
|
||||||
|
@selectedValue={{this.featureEdition.edition}}
|
||||||
|
@changed={{route-action 'toggleEdition'}}
|
||||||
|
as |radioGroup|
|
||||||
|
>
|
||||||
|
{{#each this.featureEdition.editions as |edition|}}
|
||||||
|
<dropdown.item>
|
||||||
|
<radioGroup.radio
|
||||||
|
@label={{t (concat 'editions.' edition)}}
|
||||||
|
@value={{edition}}
|
||||||
|
/>
|
||||||
|
</dropdown.item>
|
||||||
|
{{/each}}
|
||||||
|
</form.radioGroup>
|
||||||
|
</Rose::Form>
|
||||||
|
</dropdown.section>
|
||||||
|
</utilities.dropdown>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
</header.utilities>
|
</header.utilities>
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -9,28 +9,36 @@ const APP_NAME = process.env.APP_NAME || 'Boundary';
|
|||||||
const API_HOST = process.env.API_HOST || '';
|
const API_HOST = process.env.API_HOST || '';
|
||||||
const EDITION = process.env.EDITION || 'oss'; // Default edition is OSS
|
const EDITION = process.env.EDITION || 'oss'; // Default edition is OSS
|
||||||
|
|
||||||
// Object that defines edition features.
|
// Base edition declares available features, disabled by default.
|
||||||
const featureEditions = {
|
const baseEdition = {
|
||||||
oss: {
|
byow: false,
|
||||||
'static-credentials': true,
|
'byow-pki-hcp-cluster-id': false,
|
||||||
'json-credentials': true,
|
'byow-pki-upstream': false,
|
||||||
byow: true,
|
'json-credentials': false,
|
||||||
'byow-pki-hcp-cluster-id': false,
|
'ssh-target': false,
|
||||||
'byow-pki-upstream': true,
|
'static-credentials': false,
|
||||||
'vault-worker-filter': false,
|
'target-worker-filters-v2': false,
|
||||||
'target-worker-filters-v2': true,
|
'target-worker-filters-v2-ingress': false,
|
||||||
'target-worker-filters-v2-ingress': false,
|
'target-worker-filters-v2-hcp': false,
|
||||||
'target-worker-filters-v2-hcp': false,
|
'target-network-address': false,
|
||||||
'target-network-address': true,
|
'vault-worker-filter': false,
|
||||||
'credential-library-vault-ssh-certificate': false,
|
};
|
||||||
},
|
// Editions maps edition keys to their associated featuresets.
|
||||||
|
const featureEditions = {};
|
||||||
|
featureEditions.oss = {
|
||||||
|
...baseEdition,
|
||||||
|
byow: true,
|
||||||
|
'byow-pki-upstream': true,
|
||||||
|
'json-credentials': true,
|
||||||
|
'static-credentials': true,
|
||||||
|
'target-worker-filters-v2': true,
|
||||||
|
'target-network-address': true,
|
||||||
};
|
};
|
||||||
featureEditions.enterprise = {
|
featureEditions.enterprise = {
|
||||||
...featureEditions.oss,
|
...featureEditions.oss,
|
||||||
'ssh-target': true,
|
'ssh-target': true,
|
||||||
'vault-worker-filter': true,
|
|
||||||
'target-worker-filters-v2-ingress': true,
|
'target-worker-filters-v2-ingress': true,
|
||||||
'credential-library-vault-ssh-certificate': true,
|
'vault-worker-filter': true,
|
||||||
};
|
};
|
||||||
featureEditions.hcp = {
|
featureEditions.hcp = {
|
||||||
...featureEditions.enterprise,
|
...featureEditions.enterprise,
|
||||||
@@ -127,7 +135,20 @@ module.exports = function (environment) {
|
|||||||
timeout: 4000,
|
timeout: 4000,
|
||||||
},
|
},
|
||||||
|
|
||||||
featureFlags: featureEditions[EDITION],
|
selectedEdition: EDITION,
|
||||||
|
featureEditions,
|
||||||
|
featureFlags: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a set of features and enables them in all editions.
|
||||||
|
* For use in development and testing only.
|
||||||
|
*/
|
||||||
|
const enableFeaturesInAllEditions = (features = {}) => {
|
||||||
|
Object.entries(featureEditions).forEach(
|
||||||
|
([key, edition]) =>
|
||||||
|
(ENV.featureEditions[key] = { ...features, ...edition })
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (environment === 'development') {
|
if (environment === 'development') {
|
||||||
@@ -151,16 +172,12 @@ module.exports = function (environment) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enable features in development
|
// Default edition in development
|
||||||
ENV.featureFlags['static-credentials'] = true;
|
ENV.selectedEdition = 'enterprise';
|
||||||
ENV.featureFlags['json-credentials'] = true;
|
enableFeaturesInAllEditions({
|
||||||
ENV.featureFlags['ssh-target'] = true;
|
// Show edition toggle in UI in development
|
||||||
ENV.featureFlags['vault-worker-filter'] = true;
|
'dev-edition-toggle': true,
|
||||||
ENV.featureFlags['target-worker-filters-v2'] = true;
|
});
|
||||||
ENV.featureFlags['target-worker-filters-v2-ingress'] = true;
|
|
||||||
ENV.featureFlags['target-worker-filters-v2-hcp'] = true;
|
|
||||||
ENV.featureFlags['target-network-address'] = true;
|
|
||||||
ENV.featureFlags['credential-library-vault-ssh-certificate'] = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (environment === 'test') {
|
if (environment === 'test') {
|
||||||
@@ -179,14 +196,15 @@ module.exports = function (environment) {
|
|||||||
|
|
||||||
ENV.enableConfirmService = false;
|
ENV.enableConfirmService = false;
|
||||||
|
|
||||||
// Enable tests for development features
|
/**
|
||||||
ENV.featureFlags['json-credentials'] = true;
|
* Enable additional features for testing purposes.
|
||||||
ENV.featureFlags['static-credentials'] = true;
|
* [TODO] Tests should be refactored such that each test case
|
||||||
ENV.featureFlags['ssh-target'] = true;
|
* explicitly enables the features and/or edition that it needs.
|
||||||
ENV.featureFlags['vault-worker-filter'] = true;
|
* By default, tests should have no features enabled. This explicit
|
||||||
ENV.featureFlags['target-worker-filters-v2'] = true;
|
* approach ensures that test cases are easy to read and understand,
|
||||||
ENV.featureFlags['target-network-address'] = true;
|
* since their feature requirements are embedded.
|
||||||
ENV.featureFlags['credential-library-vault-ssh-certificate'] = true;
|
*/
|
||||||
|
ENV.selectedEdition = 'enterprise';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (environment === 'production') {
|
if (environment === 'production') {
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ module('Acceptance | credential-libraries | create', function (hooks) {
|
|||||||
|
|
||||||
test('cannot select vault ssh cert when feature is disabled', async function (assert) {
|
test('cannot select vault ssh cert when feature is disabled', async function (assert) {
|
||||||
const featuresService = this.owner.lookup('service:features');
|
const featuresService = this.owner.lookup('service:features');
|
||||||
featuresService.disable('credential-library-vault-ssh-certificate');
|
featuresService.disable('ssh-target');
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
await visit(urls.newCredentialLibrary);
|
await visit(urls.newCredentialLibrary);
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ module('Acceptance | credential-libraries | read', function (hooks) {
|
|||||||
`${urls.credentialLibraries}/${instances.credentialLibrary.id}`
|
`${urls.credentialLibraries}/${instances.credentialLibrary.id}`
|
||||||
);
|
);
|
||||||
const featuresService = this.owner.lookup('service:features');
|
const featuresService = this.owner.lookup('service:features');
|
||||||
featuresService.disable('credential-library-vault-ssh-certificate');
|
featuresService.disable('ssh-target');
|
||||||
await visit(urls.credentialLibraries);
|
await visit(urls.credentialLibraries);
|
||||||
|
|
||||||
assert.dom('.rose-table-body tr:nth-of-type(2) a').doesNotExist();
|
assert.dom('.rose-table-body tr:nth-of-type(2) a').doesNotExist();
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { visit, getContext } from '@ember/test-helpers';
|
||||||
|
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||||
|
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||||
|
|
||||||
|
module('Acceptance | develop', function (hooks) {
|
||||||
|
setupApplicationTest(hooks);
|
||||||
|
setupMirage(hooks);
|
||||||
|
|
||||||
|
let featuresService;
|
||||||
|
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
const { owner } = getContext();
|
||||||
|
featuresService = owner.lookup('service:features');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('edition toggle is hidden when `dev-edition-toggle` is false', async function (assert) {
|
||||||
|
await visit('/');
|
||||||
|
assert.false(featuresService.isEnabled('dev-edition-toggle'));
|
||||||
|
assert.dom('data-dev-edition-toggle').doesNotExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('edition toggle is visible when `dev-edition-toggle` is true', async function (assert) {
|
||||||
|
assert.false(featuresService.isEnabled('dev-edition-toggle'));
|
||||||
|
featuresService.enable('dev-edition-toggle');
|
||||||
|
await visit('/');
|
||||||
|
assert.dom('data-dev-edition-toggle').doesNotExist();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -125,7 +125,10 @@ module('Acceptance | targets | create', function (hooks) {
|
|||||||
await click(`[href="${urls.newTarget}"]`);
|
await click(`[href="${urls.newTarget}"]`);
|
||||||
await click('[value="ssh"]');
|
await click('[value="ssh"]');
|
||||||
await fillIn('[name="name"]', 'random string');
|
await fillIn('[name="name"]', 'random string');
|
||||||
await click('.hds-form-toggle__control', 'Egress worker filter');
|
await click(
|
||||||
|
'[name="target-worker-filter-toggle-egress_worker_filter"]',
|
||||||
|
'Egress toggle'
|
||||||
|
);
|
||||||
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
||||||
await click('[type="submit"]');
|
await click('[type="submit"]');
|
||||||
|
|
||||||
@@ -178,7 +181,10 @@ module('Acceptance | targets | create', function (hooks) {
|
|||||||
await click(`[href="${urls.newTarget}"]`);
|
await click(`[href="${urls.newTarget}"]`);
|
||||||
await click('[value="tcp"]');
|
await click('[value="tcp"]');
|
||||||
await fillIn('[name="name"]', 'random string');
|
await fillIn('[name="name"]', 'random string');
|
||||||
await click('.hds-form-toggle__control', 'Egress worker filter');
|
await click(
|
||||||
|
'[name="target-worker-filter-toggle-egress_worker_filter"]',
|
||||||
|
'Egress toggle'
|
||||||
|
);
|
||||||
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
||||||
|
|
||||||
await click('[type="submit"]');
|
await click('[type="submit"]');
|
||||||
@@ -301,7 +307,10 @@ module('Acceptance | targets | create', function (hooks) {
|
|||||||
|
|
||||||
await click(`[href="${urls.newTarget}"]`);
|
await click(`[href="${urls.newTarget}"]`);
|
||||||
await fillIn('[name="name"]', 'random string');
|
await fillIn('[name="name"]', 'random string');
|
||||||
await click('.hds-form-toggle__control', 'Egress worker filter');
|
await click(
|
||||||
|
'[name="target-worker-filter-toggle-egress_worker_filter"]',
|
||||||
|
'Egress toggle'
|
||||||
|
);
|
||||||
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
||||||
await click('.rose-form-actions [type="button"]');
|
await click('.rose-form-actions [type="button"]');
|
||||||
|
|
||||||
@@ -318,7 +327,10 @@ module('Acceptance | targets | create', function (hooks) {
|
|||||||
await click(`[href="${urls.newTarget}"]`);
|
await click(`[href="${urls.newTarget}"]`);
|
||||||
await fillIn('[name="name"]', 'random string');
|
await fillIn('[name="name"]', 'random string');
|
||||||
await click('[value="ssh"]');
|
await click('[value="ssh"]');
|
||||||
await click('.hds-form-toggle__control', 'Egress worker filter');
|
await click(
|
||||||
|
'[name="target-worker-filter-toggle-egress_worker_filter"]',
|
||||||
|
'Egress toggle'
|
||||||
|
);
|
||||||
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
await fillIn('[name="egress_worker_filter"]', 'random filter');
|
||||||
await click('.rose-form-actions [type="button"]');
|
await click('.rose-form-actions [type="button"]');
|
||||||
|
|
||||||
|
|||||||
@@ -164,16 +164,21 @@ module('Acceptance | targets | update', function (hooks) {
|
|||||||
|
|
||||||
test('hide filter input field when the `egress_worker_filter` toggled is off', async function (assert) {
|
test('hide filter input field when the `egress_worker_filter` toggled is off', async function (assert) {
|
||||||
featuresService.enable('target-worker-filters-v2');
|
featuresService.enable('target-worker-filters-v2');
|
||||||
|
featuresService.enable('target-worker-filters-v2-ingress');
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
await visit(urls.target);
|
await visit(urls.target);
|
||||||
await click('.rose-form-actions [type="button"]', 'Activate edit mode');
|
await click('.rose-form-actions [type="button"]', 'Activate edit mode');
|
||||||
await click('.hds-button[type="button"]', 'Update Filter');
|
await click('.hds-button[type="button"]', 'Update Filter');
|
||||||
await click('.hds-form-toggle__control', 'Egress worker filter');
|
await click(
|
||||||
|
'[name="target-worker-filter-toggle-egress_worker_filter"]',
|
||||||
|
'Egress toggle'
|
||||||
|
);
|
||||||
assert.dom('[name=egress_worker_filter]').isNotVisible();
|
assert.dom('[name=egress_worker_filter]').isNotVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clear `egress_worker_field` value from a target when the toggle is off and form is saved', async function (assert) {
|
test('clear `egress_worker_field` value from a target when the toggle is off and form is saved', async function (assert) {
|
||||||
featuresService.enable('target-worker-filters-v2');
|
featuresService.enable('target-worker-filters-v2');
|
||||||
|
featuresService.enable('target-worker-filters-v2-ingress');
|
||||||
assert.expect(5);
|
assert.expect(5);
|
||||||
await visit(urls.target);
|
await visit(urls.target);
|
||||||
await click('.rose-form-actions [type="button"]', 'Activate edit mode');
|
await click('.rose-form-actions [type="button"]', 'Activate edit mode');
|
||||||
@@ -186,7 +191,10 @@ module('Acceptance | targets | update', function (hooks) {
|
|||||||
'random filter string'
|
'random filter string'
|
||||||
);
|
);
|
||||||
await click('.rose-form-actions [type="button"]', 'Activate edit mode');
|
await click('.rose-form-actions [type="button"]', 'Activate edit mode');
|
||||||
await click('.hds-form-toggle__control', 'Egress worker filter');
|
await click(
|
||||||
|
'[name="target-worker-filter-toggle-egress_worker_filter"]',
|
||||||
|
'Egress toggle'
|
||||||
|
);
|
||||||
await click('.rose-form-actions [type="submit"]');
|
await click('.rose-form-actions [type="submit"]');
|
||||||
//clear egress_worker_filter when the toggle is off
|
//clear egress_worker_filter when the toggle is off
|
||||||
assert.strictEqual(currentURL(), urls.target);
|
assert.strictEqual(currentURL(), urls.target);
|
||||||
|
|||||||
@@ -137,9 +137,9 @@ test('Static Credential Store (User & Key Pair)', async ({ page }) => {
|
|||||||
if (process.env.E2E_SSH_USER != retrievedUser) {
|
if (process.env.E2E_SSH_USER != retrievedUser) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Stored User does not match. EXPECTED: ' +
|
'Stored User does not match. EXPECTED: ' +
|
||||||
process.env.E2E_SSH_USER +
|
process.env.E2E_SSH_USER +
|
||||||
', ACTUAL: ' +
|
', ACTUAL: ' +
|
||||||
retrievedUser
|
retrievedUser
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (keyData != retrievedKey) {
|
if (keyData != retrievedKey) {
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ test('Vault Credential Store (User & Key Pair)', async ({ page }) => {
|
|||||||
execSync(`vault secrets enable -path=${secretsPath} kv-v2`);
|
execSync(`vault secrets enable -path=${secretsPath} kv-v2`);
|
||||||
execSync(
|
execSync(
|
||||||
`vault kv put -mount ${secretsPath} ${secretName} ` +
|
`vault kv put -mount ${secretsPath} ${secretName} ` +
|
||||||
` username=${process.env.E2E_SSH_USER}` +
|
` username=${process.env.E2E_SSH_USER}` +
|
||||||
` private_key=@${process.env.E2E_SSH_KEY_PATH}`
|
` private_key=@${process.env.E2E_SSH_KEY_PATH}`
|
||||||
);
|
);
|
||||||
execSync(
|
execSync(
|
||||||
`vault policy write ${secretPolicyName} ./tests/e2e/tests/fixtures/kv-policy.hcl`
|
`vault policy write ${secretPolicyName} ./tests/e2e/tests/fixtures/kv-policy.hcl`
|
||||||
@@ -68,13 +68,13 @@ test('Vault Credential Store (User & Key Pair)', async ({ page }) => {
|
|||||||
const vaultToken = JSON.parse(
|
const vaultToken = JSON.parse(
|
||||||
execSync(
|
execSync(
|
||||||
`vault token create` +
|
`vault token create` +
|
||||||
` -no-default-policy=true` +
|
` -no-default-policy=true` +
|
||||||
` -policy=${boundaryPolicyName}` +
|
` -policy=${boundaryPolicyName}` +
|
||||||
` -policy=${secretPolicyName}` +
|
` -policy=${secretPolicyName}` +
|
||||||
` -orphan=true` +
|
` -orphan=true` +
|
||||||
` -period=20m` +
|
` -period=20m` +
|
||||||
` -renewable=true` +
|
` -renewable=true` +
|
||||||
` -format=json`
|
` -format=json`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const clientToken = vaultToken.auth.client_token;
|
const clientToken = vaultToken.auth.client_token;
|
||||||
@@ -175,9 +175,9 @@ test('Vault Credential Store (User & Key Pair)', async ({ page }) => {
|
|||||||
if (process.env.E2E_SSH_USER != retrievedUser) {
|
if (process.env.E2E_SSH_USER != retrievedUser) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Stored User does not match. EXPECTED: ' +
|
'Stored User does not match. EXPECTED: ' +
|
||||||
process.env.E2E_SSH_USER +
|
process.env.E2E_SSH_USER +
|
||||||
', ACTUAL: ' +
|
', ACTUAL: ' +
|
||||||
retrievedUser
|
retrievedUser
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (keyData != retrievedKey) {
|
if (keyData != retrievedKey) {
|
||||||
|
|||||||
@@ -128,9 +128,9 @@ test.describe('AWS', async () => {
|
|||||||
if (!hostsAreVisible) {
|
if (!hostsAreVisible) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Hosts are not visible. EXPECTED: ' +
|
'Hosts are not visible. EXPECTED: ' +
|
||||||
expectedHosts.length +
|
expectedHosts.length +
|
||||||
', ACTUAL: ' +
|
', ACTUAL: ' +
|
||||||
rowCount
|
rowCount
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,9 +216,9 @@ test.describe('AWS', async () => {
|
|||||||
if (!hostsAreVisible) {
|
if (!hostsAreVisible) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Hosts are not visible. EXPECTED: ' +
|
'Hosts are not visible. EXPECTED: ' +
|
||||||
expectedHosts.length +
|
expectedHosts.length +
|
||||||
', ACTUAL: ' +
|
', ACTUAL: ' +
|
||||||
rowCount
|
rowCount
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,18 +61,18 @@ test('Verify session created then cancel the session', async ({ page }) => {
|
|||||||
|
|
||||||
connect = exec(
|
connect = exec(
|
||||||
'boundary connect' +
|
'boundary connect' +
|
||||||
' -target-id=' +
|
' -target-id=' +
|
||||||
target.id +
|
target.id +
|
||||||
' -exec /usr/bin/ssh --' +
|
' -exec /usr/bin/ssh --' +
|
||||||
' -l ' +
|
' -l ' +
|
||||||
process.env.E2E_SSH_USER +
|
process.env.E2E_SSH_USER +
|
||||||
' -i ' +
|
' -i ' +
|
||||||
process.env.E2E_SSH_KEY_PATH +
|
process.env.E2E_SSH_KEY_PATH +
|
||||||
' -o UserKnownHostsFile=/dev/null' +
|
' -o UserKnownHostsFile=/dev/null' +
|
||||||
' -o StrictHostKeyChecking=no' +
|
' -o StrictHostKeyChecking=no' +
|
||||||
' -o IdentitiesOnly=yes' + // forces the use of the provided key
|
' -o IdentitiesOnly=yes' + // forces the use of the provided key
|
||||||
' -p {{boundary.port}}' +
|
' -p {{boundary.port}}' +
|
||||||
' {{boundary.ip}}'
|
' {{boundary.ip}}'
|
||||||
);
|
);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ module('Unit | Abilities | credential-library', function (hooks) {
|
|||||||
|
|
||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
features = this.owner.lookup('service:features');
|
features = this.owner.lookup('service:features');
|
||||||
features.enable('credential-library-vault-ssh-certificate');
|
features.enable('ssh-target');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can read credential library type when authorized and feature is enabled', function (assert) {
|
test('can read credential library type when authorized and feature is enabled', function (assert) {
|
||||||
@@ -51,7 +51,7 @@ module('Unit | Abilities | credential-library', function (hooks) {
|
|||||||
const canService = this.owner.lookup('service:can');
|
const canService = this.owner.lookup('service:can');
|
||||||
const store = this.owner.lookup('service:store');
|
const store = this.owner.lookup('service:store');
|
||||||
const featuresService = this.owner.lookup('service:features');
|
const featuresService = this.owner.lookup('service:features');
|
||||||
featuresService.disable('credential-library-vault-ssh-certificate');
|
featuresService.disable('ssh-target');
|
||||||
const credentialLibrary = store.createRecord('credential-library', {
|
const credentialLibrary = store.createRecord('credential-library', {
|
||||||
authorized_actions: [],
|
authorized_actions: [],
|
||||||
type: TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE,
|
type: TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE,
|
||||||
@@ -66,7 +66,7 @@ module('Unit | Abilities | credential-library', function (hooks) {
|
|||||||
const canService = this.owner.lookup('service:can');
|
const canService = this.owner.lookup('service:can');
|
||||||
const store = this.owner.lookup('service:store');
|
const store = this.owner.lookup('service:store');
|
||||||
const featuresService = this.owner.lookup('service:features');
|
const featuresService = this.owner.lookup('service:features');
|
||||||
featuresService.disable('credential-library-vault-ssh-certificate');
|
featuresService.disable('ssh-target');
|
||||||
const credentialLibrary = store.createRecord('credential-library', {
|
const credentialLibrary = store.createRecord('credential-library', {
|
||||||
authorized_actions: ['read'],
|
authorized_actions: ['read'],
|
||||||
type: TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE,
|
type: TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE,
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import Application from '@ember/application';
|
||||||
|
|
||||||
|
import config from 'admin/config/environment';
|
||||||
|
import { initialize } from 'admin/instance-initializers/feature-edition';
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import Resolver from 'ember-resolver';
|
||||||
|
import { run } from '@ember/runloop';
|
||||||
|
|
||||||
|
module('Unit | Instance Initializer | feature-edition', function (hooks) {
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
this.TestApplication = class TestApplication extends Application {
|
||||||
|
modulePrefix = config.modulePrefix;
|
||||||
|
podModulePrefix = config.podModulePrefix;
|
||||||
|
Resolver = Resolver;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.TestApplication.instanceInitializer({
|
||||||
|
name: 'initializer under test',
|
||||||
|
initialize,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.application = this.TestApplication.create({
|
||||||
|
autoboot: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.instance = this.application.buildInstance();
|
||||||
|
});
|
||||||
|
hooks.afterEach(function () {
|
||||||
|
run(this.instance, 'destroy');
|
||||||
|
run(this.application, 'destroy');
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Replace this with your real tests.
|
||||||
|
test('it works', async function (assert) {
|
||||||
|
await this.instance.boot();
|
||||||
|
|
||||||
|
assert.ok(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'admin/tests/helpers';
|
||||||
|
|
||||||
|
module('Unit | Service | features', function (hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
// TODO: Replace this with your real tests.
|
||||||
|
test('it exists', function (assert) {
|
||||||
|
let service = this.owner.lookup('service:features');
|
||||||
|
assert.ok(service);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user