feat: 🎸 Added GCP host sets and hosts (#2655)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15994
This commit is contained in:
1 parent
3b6a421af2
commit
390d179917
16 files changed
+412
-97
No files matched your search
@@ -63,6 +63,10 @@ export default class GeneratedHostSetModel extends BaseModel {
|
||||
plugin;
|
||||
|
||||
@attr('number', {
|
||||
for: {
|
||||
type: 'plugin',
|
||||
name: ['aws', 'azure', 'gcp'],
|
||||
},
|
||||
description:
|
||||
"The number of seconds between the time boundary syncs the hosts in this set using this host set's plugin. If not provided a system determined default is used",
|
||||
})
|
||||
@@ -79,12 +83,20 @@ export default class GeneratedHostSetModel extends BaseModel {
|
||||
host_ids;
|
||||
|
||||
@attr('string-array', {
|
||||
for: {
|
||||
type: 'plugin',
|
||||
name: ['aws', 'azure', 'gcp'],
|
||||
},
|
||||
emptyArrayIfMissing: true,
|
||||
})
|
||||
preferred_endpoints;
|
||||
|
||||
// AWS & GCP specific
|
||||
@attr('string-array', {
|
||||
for: {
|
||||
type: 'plugin',
|
||||
name: ['aws', 'gcp'],
|
||||
},
|
||||
isNestedAttribute: true,
|
||||
emptyArrayIfMissing: true,
|
||||
})
|
||||
@@ -92,6 +104,10 @@ export default class GeneratedHostSetModel extends BaseModel {
|
||||
|
||||
// Azure specific, comes in from API as "filter". This is to avoid collisions with filter
|
||||
@attr('string', {
|
||||
for: {
|
||||
type: 'plugin',
|
||||
name: 'azure',
|
||||
},
|
||||
description: '',
|
||||
isNestedAttribute: true,
|
||||
})
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
const fieldByType = {
|
||||
aws: ['preferred_endpoints', 'filters', 'sync_interval_seconds'],
|
||||
azure: ['preferred_endpoints', 'filter_string', 'sync_interval_seconds'],
|
||||
};
|
||||
export default class HostSetSerializer extends ApplicationSerializer {
|
||||
// =properties
|
||||
|
||||
@@ -63,16 +59,8 @@ export default class HostSetSerializer extends ApplicationSerializer {
|
||||
return serialized;
|
||||
}
|
||||
|
||||
serializeAttribute(snapshot, json, key, attribute) {
|
||||
serializeAttribute(snapshot, json, key) {
|
||||
const value = super.serializeAttribute(...arguments);
|
||||
const { isPlugin, compositeType } = snapshot.record;
|
||||
const { options } = attribute;
|
||||
|
||||
if (isPlugin && options.isNestedAttribute) {
|
||||
if (!fieldByType[compositeType].includes(key)) {
|
||||
delete json.attributes[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'filter_string') {
|
||||
const { filter_string } = json.attributes;
|
||||
|
||||
@@ -53,8 +53,8 @@ export default factory.extend({
|
||||
worker_filter: function () {
|
||||
if (
|
||||
this.type === TYPE_HOST_CATALOG_DYNAMIC &&
|
||||
(this.plugin?.name === TYPE_HOST_CATALOG_PLUGIN_AWS ||
|
||||
this.plugin?.name === TYPE_HOST_CATALOG_PLUGIN_GCP)
|
||||
(this.plugin?.name === TYPE_HOST_CATALOG_PLUGIN_AWS ||
|
||||
this.plugin?.name === TYPE_HOST_CATALOG_PLUGIN_GCP)
|
||||
) {
|
||||
return `"${faker.word.noun()}" in "${faker.system.directoryPath()}"`;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,22 @@ import factory from '../generated/factories/host-set';
|
||||
import permissions from '../helpers/permissions';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import generateId from '../helpers/id';
|
||||
import {
|
||||
TYPE_HOST_CATALOG_PLUGIN_AWS,
|
||||
TYPE_HOST_CATALOG_PLUGIN_AZURE,
|
||||
TYPE_HOST_CATALOG_PLUGIN_GCP,
|
||||
} from 'api/models/host-catalog';
|
||||
|
||||
function generateFilters(prefix) {
|
||||
const filtersAmount = faker.number.int({ min: 1, max: 5 });
|
||||
let filters = [];
|
||||
for (let i = 0; i < filtersAmount; ++i) {
|
||||
filters.push(`${prefix}${faker.word.words(1)}=${faker.word.words(1)}`);
|
||||
}
|
||||
return {
|
||||
filters,
|
||||
};
|
||||
}
|
||||
|
||||
export default factory.extend({
|
||||
id: () => generateId('hs_'),
|
||||
@@ -42,22 +58,15 @@ export default factory.extend({
|
||||
},
|
||||
|
||||
attributes() {
|
||||
// AWS specific
|
||||
if (this.plugin?.name === 'aws') {
|
||||
const filtersAmount = faker.number.int({ min: 1, max: 5 });
|
||||
let filters = [];
|
||||
for (let i = 0; i < filtersAmount; ++i) {
|
||||
filters.push(`${faker.word.words(1)}=${faker.word.words(1)}`);
|
||||
}
|
||||
return {
|
||||
filters,
|
||||
};
|
||||
}
|
||||
// Azure specific
|
||||
if (this.plugin?.name === 'azure') {
|
||||
return {
|
||||
filter: `${faker.database.column()}=${faker.database.collation()}`,
|
||||
};
|
||||
switch (this.plugin?.name) {
|
||||
case TYPE_HOST_CATALOG_PLUGIN_AWS:
|
||||
return generateFilters('tag:');
|
||||
case TYPE_HOST_CATALOG_PLUGIN_AZURE:
|
||||
return {
|
||||
filter: `${faker.database.column()}=${faker.database.collation()}`,
|
||||
};
|
||||
case TYPE_HOST_CATALOG_PLUGIN_GCP:
|
||||
return generateFilters('labels.');
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -7,24 +7,28 @@ import factory from '../generated/factories/host';
|
||||
import permissions from '../helpers/permissions';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import generateId from '../helpers/id';
|
||||
import {
|
||||
TYPE_HOST_CATALOG_STATIC,
|
||||
TYPE_HOST_CATALOG_DYNAMIC,
|
||||
} from 'api/models/host-catalog';
|
||||
|
||||
export default factory.extend({
|
||||
id: () => generateId('h_'),
|
||||
name() {
|
||||
if (this.type === 'static') return faker.word.words();
|
||||
if (this.type === TYPE_HOST_CATALOG_STATIC) return faker.word.words();
|
||||
},
|
||||
type() {
|
||||
return this.hostCatalog?.type || factory.attrs.type;
|
||||
},
|
||||
authorized_actions: function () {
|
||||
const isStatic = this.type === 'static';
|
||||
const isStatic = this.type === TYPE_HOST_CATALOG_STATIC;
|
||||
const defaults = ['no-op', 'read'];
|
||||
// Only static allows update/delete at this time.
|
||||
if (isStatic) defaults.push('update', 'delete');
|
||||
return permissions.authorizedActionsFor('host') || defaults;
|
||||
},
|
||||
plugin() {
|
||||
if (this.type === 'plugin') {
|
||||
if (this.type === TYPE_HOST_CATALOG_DYNAMIC) {
|
||||
return this.hostCatalog.plugin;
|
||||
}
|
||||
},
|
||||
@@ -38,12 +42,12 @@ export default factory.extend({
|
||||
},
|
||||
// Return external fields only for plugins
|
||||
external_id() {
|
||||
if (this.type === 'plugin') {
|
||||
if (this.type === TYPE_HOST_CATALOG_DYNAMIC) {
|
||||
return faker.string.uuid();
|
||||
}
|
||||
},
|
||||
external_name(i) {
|
||||
if (this.type === 'plugin') {
|
||||
if (this.type === TYPE_HOST_CATALOG_DYNAMIC) {
|
||||
return `${this.plugin.name} provided name ${i}`;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -65,3 +65,10 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility classes
|
||||
.container-inputs__margin-bottom {
|
||||
input:not(:last-child) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
@@ -275,9 +275,11 @@ host-set:
|
||||
filter:
|
||||
label: Filter
|
||||
aws:
|
||||
help: Create a filter to select resources using values such as tag name or tag value. The AWS filter format is tag:<tag-name>=<tag-value>. For example:tag:application=production
|
||||
help: 'Create a filter to select resources using values such as tag name or tag value. The AWS filter format is tag:<tag-name>=<tag-value>. For example: tag:application=production'
|
||||
azure:
|
||||
help: Create a filter to select resources using values such as tag name or tag value. The Azure filter format is:tagName eq ‘<tag-name>’ and tagValue eq ‘<tag-value>'. For example:tagName eq 'application' and tagValue eq 'production'
|
||||
gcp:
|
||||
help: 'Create a filter to select resources using values such as label name or label value. The GCP filter format is labels.<label-name>=<label-value>. For example: labels.application=production'
|
||||
preferred_endpoints:
|
||||
label: Preferred Endpoints
|
||||
help: Preferred address at which the host should be accessed when multiple options are present, in cidr:<valid IPv4/6 CIDR> or dns:<globbed name> format.
|
||||
|
||||
@@ -51,16 +51,11 @@
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
<InfoField @value={{@model.type}} disabled={{form.disabled}} as |F|>
|
||||
<InfoField @value={{@model.type}} as |F|>
|
||||
<F.Label>{{t 'form.type.label'}}</F.Label>
|
||||
</InfoField>
|
||||
|
||||
<InfoField
|
||||
@value={{t 'descriptions.provider'}}
|
||||
@icon='aws-color'
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<InfoField @value={{t 'descriptions.provider'}} @icon='aws-color' as |F|>
|
||||
<F.Label>{{t 'titles.provider'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-catalog.types.aws'}}
|
||||
|
||||
@@ -51,16 +51,11 @@
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
<InfoField @value={{@model.type}} readonly={{true}} as |F|>
|
||||
<InfoField @value={{@model.type}} as |F|>
|
||||
<F.Label>{{t 'form.type.label'}}</F.Label>
|
||||
</InfoField>
|
||||
|
||||
<InfoField
|
||||
@value={{t 'descriptions.provider'}}
|
||||
@icon='azure-color'
|
||||
readonly={{true}}
|
||||
as |F|
|
||||
>
|
||||
<InfoField @value={{t 'descriptions.provider'}} @icon='azure-color' as |F|>
|
||||
<F.Label>{{t 'titles.provider'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-catalog.types.azure'}}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Rose::Form
|
||||
@edit={{@edit}}
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
<Hds::Form::TextInput::Field
|
||||
name='name'
|
||||
@value={{@model.name}}
|
||||
@isOptional={{true}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
|
||||
{{#if @model.errors.name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::Textarea::Field
|
||||
name='description'
|
||||
@value={{@model.description}}
|
||||
@isOptional={{true}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
{{#if @model.errors.description}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
<InfoField @value={{@model.type}} as |F|>
|
||||
<F.Label>{{t 'form.type.label'}}</F.Label>
|
||||
</InfoField>
|
||||
|
||||
<InfoField @value={{t 'descriptions.provider'}} @icon='gcp-color' as |F|>
|
||||
<F.Label>{{t 'titles.provider'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-catalog.types.gcp'}}
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@disabled={{form.disabled}}
|
||||
>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.host-set.form.preferred_endpoints.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-set.form.preferred_endpoints.help'}}
|
||||
</F.HelperText>
|
||||
|
||||
{{#if @model.errors.preferred_endpoints}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.preferred_endpoints as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='preferred_endpoints'
|
||||
@options={{@model.preferred_endpoints}}
|
||||
@model={{@model}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@disabled={{form.disabled}}
|
||||
>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.host-set.form.filter.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-set.form.filter.gcp.help'}}
|
||||
</F.HelperText>
|
||||
|
||||
{{#if @model.errors.filters}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.filters as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='filters'
|
||||
@options={{@model.filters}}
|
||||
@model={{@model}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
name='sync_interval_seconds'
|
||||
@value={{@model.sync_interval_seconds}}
|
||||
@isInvalid={{@model.errors.sync_interval_seconds}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'sync_interval_seconds')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.sync-interval.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'form.sync-interval.help'}}
|
||||
<Hds::Link::Inline @href={{doc-url 'host-set.sync-interval-seconds'}}>
|
||||
{{t 'actions.learn-more'}}
|
||||
</Hds::Link::Inline>
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.sync_interval_seconds}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.sync_interval_seconds as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Rose::Form>
|
||||
@@ -30,13 +30,6 @@
|
||||
>
|
||||
<F.Label>{{t 'form.external_name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.external_name.help'}}</F.HelperText>
|
||||
{{#if @model.errors.external_name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.external_name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
{{/if}}
|
||||
|
||||
@@ -49,13 +42,6 @@
|
||||
>
|
||||
<F.Label>{{t 'form.external_id.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.external_id.help'}}</F.HelperText>
|
||||
{{#if @model.errors.external_id}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.external_id as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::Textarea::Field
|
||||
@@ -67,13 +53,6 @@
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
{{#if @model.errors.description}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
<InfoField
|
||||
@@ -88,7 +67,7 @@
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
|
||||
<Hds::Form::Fieldset as |F|>
|
||||
<Hds::Form::Fieldset class='container-inputs__margin-bottom' as |F|>
|
||||
<F.Legend>{{t 'form.ip_addresses.label'}}</F.Legend>
|
||||
<F.HelperText>{{t 'form.ip_addresses.help'}} </F.HelperText>
|
||||
{{#each @model.ip_addresses as |ip_address|}}
|
||||
@@ -102,7 +81,7 @@
|
||||
{{/each}}
|
||||
</Hds::Form::Fieldset>
|
||||
|
||||
<Hds::Form::Fieldset as |F|>
|
||||
<Hds::Form::Fieldset class='container-inputs__margin-bottom' as |F|>
|
||||
<F.Legend>{{t 'form.dns_names.label'}}</F.Legend>
|
||||
<F.HelperText>{{t 'form.dns_names.help'}} </F.HelperText>
|
||||
{{#each @model.dns_names as |dns_name|}}
|
||||
|
||||
@@ -30,13 +30,6 @@
|
||||
>
|
||||
<F.Label>{{t 'form.external_name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.external_name.help'}}</F.HelperText>
|
||||
{{#if @model.errors.external_name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.external_name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
{{/if}}
|
||||
|
||||
@@ -49,13 +42,6 @@
|
||||
>
|
||||
<F.Label>{{t 'form.external_id.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.external_id.help'}}</F.HelperText>
|
||||
{{#if @model.errors.external_id}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.external_id as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::Textarea::Field
|
||||
@@ -67,13 +53,6 @@
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
{{#if @model.errors.description}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
<InfoField
|
||||
@@ -88,7 +67,7 @@
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
|
||||
<Hds::Form::Fieldset as |F|>
|
||||
<Hds::Form::Fieldset class='container-inputs__margin-bottom' as |F|>
|
||||
<F.Legend>{{t 'form.ip_addresses.label'}}</F.Legend>
|
||||
<F.HelperText>{{t 'form.ip_addresses.help'}} </F.HelperText>
|
||||
{{#each @model.ip_addresses as |ip_address|}}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{@model.type}}
|
||||
name='type'
|
||||
disabled={{true}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.type.label'}}</F.Label>
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{#if @model.external_name}}
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{@model.external_name}}
|
||||
@isInvalid={{@model.errors.external_name}}
|
||||
name='external_name'
|
||||
disabled={{true}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.external_name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.external_name.help'}}</F.HelperText>
|
||||
</Hds::Form::TextInput::Field>
|
||||
{{/if}}
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{@model.external_id}}
|
||||
@isInvalid={{@model.errors.external_id}}
|
||||
name='external_id'
|
||||
disabled={{true}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.external_id.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.external_id.help'}}</F.HelperText>
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::Textarea::Field
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
name='description'
|
||||
disabled={{true}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
<InfoField
|
||||
@value={{t 'descriptions.provider'}}
|
||||
@icon='gcp-color'
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'titles.provider'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-catalog.types.gcp'}}
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
|
||||
<Hds::Form::Fieldset class='container-inputs__margin-bottom' as |F|>
|
||||
<F.Legend>{{t 'form.ip_addresses.label'}}</F.Legend>
|
||||
<F.HelperText>{{t 'form.ip_addresses.help'}} </F.HelperText>
|
||||
{{#each @model.ip_addresses as |ip_address|}}
|
||||
<F.Control>
|
||||
<Hds::Form::TextInput::Base
|
||||
@value={{ip_address}}
|
||||
disabled={{true}}
|
||||
@width='unset'
|
||||
/>
|
||||
</F.Control>
|
||||
{{/each}}
|
||||
</Hds::Form::Fieldset>
|
||||
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Rose::Form>
|
||||
+18
-1
@@ -18,6 +18,7 @@ export default class ScopesScopeHostCatalogsHostCatalogHostSetsIndexController e
|
||||
@service can;
|
||||
@service intl;
|
||||
@service router;
|
||||
@service store;
|
||||
|
||||
// =attributes
|
||||
|
||||
@@ -74,8 +75,24 @@ export default class ScopesScopeHostCatalogsHostCatalogHostSetsIndexController e
|
||||
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
||||
)
|
||||
async save(hostSet) {
|
||||
// Fetch newest host set as updates to host set attributes cause an async db update which
|
||||
// updates the version again and can cause a version mismatch if the host set is updated
|
||||
// again and we haven't fetched the newest version.
|
||||
if (this.can.can('read host-set', hostSet)) {
|
||||
const newestHostSet = await this.store.findRecord(
|
||||
'host-set',
|
||||
hostSet.id,
|
||||
{
|
||||
reload: true,
|
||||
},
|
||||
);
|
||||
|
||||
hostSet.version = newestHostSet.version;
|
||||
}
|
||||
|
||||
await hostSet.save();
|
||||
if (this.can.can('read model', hostSet)) {
|
||||
|
||||
if (this.can.can('read host-set', hostSet)) {
|
||||
await this.router.transitionTo(
|
||||
'scopes.scope.host-catalogs.host-catalog.host-sets.host-set',
|
||||
hostSet,
|
||||
|
||||
@@ -150,6 +150,37 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) {
|
||||
assert.deepEqual(hostSet.syncIntervalSeconds, 10);
|
||||
});
|
||||
|
||||
test('can create new gcp host set', async function (assert) {
|
||||
instances.hostCatalog = this.server.create('host-catalog', {
|
||||
scope: instances.scopes.project,
|
||||
type: 'plugin',
|
||||
plugin: {
|
||||
id: `plugin-id-1`,
|
||||
name: 'gcp',
|
||||
},
|
||||
});
|
||||
|
||||
const count = getHostSetCount();
|
||||
await visit(
|
||||
`${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/new`,
|
||||
);
|
||||
const name = 'gcp host set';
|
||||
await fillIn(NAME_SELECTOR, name);
|
||||
await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'endpoint');
|
||||
await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR);
|
||||
await fillIn(FILTER_TEXT_INPUT_SELECTOR, 'filter_test');
|
||||
await click(FILTER_BUTTON_SELECTOR);
|
||||
|
||||
await fillIn(SYNC_INTERVAL_SELECTOR, 10);
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(getHostSetCount(), count + 1);
|
||||
const hostSet = this.server.schema.hostSets.findBy({ name });
|
||||
assert.deepEqual(hostSet.preferredEndpoints, ['endpoint']);
|
||||
assert.deepEqual(hostSet.attributes.filters, ['filter_test']);
|
||||
assert.deepEqual(hostSet.syncIntervalSeconds, 10);
|
||||
});
|
||||
|
||||
test('Users cannot create a new host set without proper authorization', async function (assert) {
|
||||
instances.hostCatalog.authorized_collection_actions['host-sets'] = [];
|
||||
await visit(urls.hostCatalog);
|
||||
|
||||
@@ -247,6 +247,52 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
assert.deepEqual(hostSet.syncIntervalSeconds, 10);
|
||||
});
|
||||
|
||||
test('can save changes to an existing gcp host-set', async function (assert) {
|
||||
await visit(urls.awshostSet);
|
||||
|
||||
await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
|
||||
|
||||
const name = 'gcp host set';
|
||||
await fillIn('[name="name"]', name);
|
||||
|
||||
const endpointList = findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR);
|
||||
|
||||
for (const element of endpointList) {
|
||||
await click(element);
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR).length,
|
||||
0,
|
||||
);
|
||||
|
||||
await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'sample endpoint');
|
||||
await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR);
|
||||
|
||||
// Remove all the filters
|
||||
const filterList = await Promise.all(
|
||||
findAll(FILTER_REMOVE_BUTTON_SELECTOR),
|
||||
);
|
||||
for (const element of filterList) {
|
||||
await click(element);
|
||||
}
|
||||
|
||||
assert.strictEqual(findAll(FILTER_REMOVE_BUTTON_SELECTOR).length, 0);
|
||||
await fillIn(FILTER_TEXT_INPUT_SELECTOR, 'sample filters');
|
||||
await click(FILTER_BUTTON_SELECTOR);
|
||||
|
||||
await fillIn(SYNC_INTERVAL_SELECTOR, 10);
|
||||
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.awshostSet);
|
||||
const hostSet = this.server.schema.hostSets.findBy({ name });
|
||||
assert.strictEqual(hostSet.name, name);
|
||||
assert.deepEqual(hostSet.preferredEndpoints, ['sample endpoint']);
|
||||
assert.deepEqual(hostSet.attributes.filters, ['sample filters']);
|
||||
assert.deepEqual(hostSet.syncIntervalSeconds, 10);
|
||||
});
|
||||
|
||||
test('cannot make changes to an existing host without proper authorization', async function (assert) {
|
||||
instances.hostSet.authorized_actions =
|
||||
instances.hostSet.authorized_actions.filter((item) => item !== 'update');
|
||||
|
||||
Reference in new issue
Block a user