Types for aws host catalogs (#2514)

assume role dynamic AWS credentials
This commit is contained in:
Dharini Jeeva authored and GitHub committed 2024-10-09 14:49:22 -07:00
1 parent 0d55812f64
commit 965e82473d
11 files changed
+538 -89

No files matched your search

@@ -66,7 +66,6 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// AWS specific
@attr('string', {
for: 'plugin',
description:
'An expression used to filter the workers that have network access to a service that is hosting the external object store.',
})
@@ -78,6 +77,7 @@ export default class GeneratedHostCatalogModel extends BaseModel {
})
region;
// AWS static credentials
@attr('string', {
isNestedSecret: true,
description: '',
@@ -90,6 +90,31 @@ export default class GeneratedHostCatalogModel extends BaseModel {
})
secret_access_key;
// AWS dynamic credentials
@attr('string', {
isNestedAttribute: true,
description: 'The role ARN to use.',
})
role_arn;
@attr('string', {
isNestedAttribute: true,
description: 'The role external ID to use.',
})
role_external_id;
@attr('string', {
isNestedAttribute: true,
description: 'The role session to use.',
})
role_session_name;
@attr('object-as-array', {
isNestedAttribute: true,
description: 'The role tags to use.',
})
role_tags;
// Azure specific
@attr('string', {
+32
View File
@@ -19,9 +19,18 @@ export const TYPES_HOST_CATALOG_PLUGIN = [
TYPE_HOST_CATALOG_PLUGIN_AZURE,
];
export const TYPE_CREDENTIAL_STATIC = 'static-credential';
export const TYPE_CREDENTIAL_DYNAMIC = 'dynamic-credential';
export const TYPES_CREDENTIALS = Object.freeze([
TYPE_CREDENTIAL_STATIC,
TYPE_CREDENTIAL_DYNAMIC,
]);
export default class HostCatalogModel extends GeneratedHostCatalogModel {
// =attributes
#credentialType;
/**
* True if the host catalog is static.
* @type {boolean}
@@ -38,6 +47,29 @@ export default class HostCatalogModel extends GeneratedHostCatalogModel {
return this.type === 'plugin';
}
/**
* Gets a value from credentialType
* @type {string}
*/
get credentialType() {
if (!this.#credentialType) {
if (this.role_arn) {
this.#credentialType = TYPE_CREDENTIAL_DYNAMIC;
} else {
this.#credentialType = TYPE_CREDENTIAL_STATIC;
}
}
return this.#credentialType;
}
/**
* Sets a value to credentialType
* @type {string}
*/
set credentialType(type) {
this.#credentialType = type;
}
/**
* True if the host catalog is an unknown type.
* @type {boolean}
+46 -25
View File
@@ -4,30 +4,53 @@
*/
import ApplicationSerializer from './application';
import {
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_HOST_CATALOG_PLUGIN_AZURE,
} from '../models/host-catalog';
const fieldsByType = {
aws: [
'disable_credential_rotation',
'region',
// These fields are Azure specific
const azureFields = [
'disable_credential_rotation',
'tenant_id',
'client_id',
'subscription_id',
'secret_id',
'secret_value',
];
// These fields are AWS specific, AWS provider now has two credentialTypes: static and dynamic [not to be confused with static and dynamic host-catalogs]
const AWSfieldsWithCredentialType = {
'static-credential': [
'access_key_id',
'secret_access_key',
'region',
'disable_credential_rotation',
'worker_filter',
],
azure: [
'dynamic-credential': [
'role_arn',
'role_external_id',
'role_session_name',
'role_tags',
'region',
'disable_credential_rotation',
'tenant_id',
'client_id',
'subscription_id',
'secret_id',
'secret_value',
'worker_filter',
],
};
export default class HostCatalogSerializer extends ApplicationSerializer {
// =methods
serialize(snapshot) {
const { compositeType } = snapshot.record;
const serialized = super.serialize(...arguments);
switch (snapshot.record.compositeType) {
// This is a hack to remove the worker_filter field from the serialized data if the compositeType is not AWS
// Ideally we should refactor the model to include the compositeType using `for` attribute
if (compositeType !== TYPE_HOST_CATALOG_PLUGIN_AWS) {
delete serialized.worker_filter;
}
switch (compositeType) {
case 'static':
return this.serializeStatic(...arguments);
default:
@@ -37,31 +60,28 @@ export default class HostCatalogSerializer extends ApplicationSerializer {
serializeAttribute(snapshot, json, key, attribute) {
const value = super.serializeAttribute(...arguments);
const { isPlugin, compositeType } = snapshot.record;
const { compositeType, credentialType, isPlugin } = snapshot.record;
const { options } = attribute;
// Delete any fields that don't belong to the compositeType
if (
isPlugin &&
json &&
options?.for &&
!options.for.includes(compositeType) &&
!fieldsByType[compositeType].includes(key)
) {
delete json[key];
}
const fields =
compositeType === TYPE_HOST_CATALOG_PLUGIN_AZURE
? azureFields
: AWSfieldsWithCredentialType[credentialType];
// Delete any nested attribute fields that don't belong to the record type
if (isPlugin && options.isNestedAttribute && json.attributes) {
if (!fieldsByType[compositeType].includes(key)) {
if (!fields.includes(key)) {
delete json.attributes[key];
}
}
// Delete any secret fields that don't belong to the record type
if (isPlugin && options.isNestedSecret && json.secrets) {
if (!fieldsByType[compositeType].includes(key)) {
if (!fields.includes(key)) {
delete json.secrets[key];
if (json['secrets'] && Object.keys(json.secrets).length === 0) {
delete json.secrets;
}
}
}
return value;
@@ -72,6 +92,7 @@ export default class HostCatalogSerializer extends ApplicationSerializer {
// Delete unnecessary fields for static host-catalog
delete serialized.attributes;
delete serialized.secrets;
delete serialized.worker_filter;
return serialized;
}
}
@@ -5,6 +5,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import {
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_CREDENTIAL_STATIC,
TYPE_CREDENTIAL_DYNAMIC,
} from 'api/models/host-catalog';
module('Unit | Serializer | host catalog', function (hooks) {
setupTest(hooks);
@@ -31,15 +36,104 @@ module('Unit | Serializer | host catalog', function (hooks) {
assert.deepEqual(record.serialize(), expectedResult);
});
test('it serializes a new aws plugin as expected, ignoring azure fields', async function (assert) {
test('it serializes a new aws static plugin as expected', async function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('host-catalog', {
compositeType: 'aws',
compositeType: TYPE_HOST_CATALOG_PLUGIN_AWS,
credentialType: TYPE_CREDENTIAL_STATIC,
name: 'AWS',
description: 'this is a Aws plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
// these are AWS fields and should be included
region: 'west',
access_key_id: 'foobars',
secret_access_key: 'testing',
// these are Azure fields and should be excluded
tenant_id: 'a1b2c3',
client_id: 'a1b2c3',
subscription_id: 'a1b2c3',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
role_arn: 'test',
role_external_id: 'test',
role_session_name: 'test',
role_tags: [
{ key: 'Project', value: 'Automation' },
{ key: 'foo', value: 'bar' },
],
});
const expectedResult = {
name: 'AWS',
description: 'this is a Aws plugin host-catalog',
type: 'plugin',
worker_filter: 'workerfilter',
attributes: {
disable_credential_rotation: true,
region: 'west',
},
secrets: {
access_key_id: 'foobars',
secret_access_key: 'testing',
},
};
assert.deepEqual(record.serialize(), expectedResult);
});
test('it serializes a new aws dynamic plugin as expected', async function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('host-catalog', {
compositeType: TYPE_HOST_CATALOG_PLUGIN_AWS,
name: 'AWS',
credentialType: TYPE_CREDENTIAL_DYNAMIC,
description: 'this is a Aws plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
// these are AWS fields and should be included
region: 'west',
access_key_id: 'foobars',
secret_access_key: 'testing',
// these are Azure fields and should be excluded
tenant_id: 'a1b2c3',
client_id: 'a1b2c3',
subscription_id: 'a1b2c3',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
role_arn: 'test',
role_external_id: 'test',
role_session_name: 'test',
role_tags: [
{ key: 'Project', value: 'Automation' },
{ key: 'foo', value: 'bar' },
],
});
const expectedResult = {
name: 'AWS',
description: 'this is a Aws plugin host-catalog',
type: 'plugin',
worker_filter: 'workerfilter',
attributes: {
disable_credential_rotation: true,
region: 'west',
role_external_id: 'test',
role_session_name: 'test',
role_arn: 'test',
role_tags: { Project: 'Automation', foo: 'bar' },
},
};
assert.deepEqual(record.serialize(), expectedResult);
});
test('it serializes a new aws static plugin as expected, ignoring azure fields', async function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('host-catalog', {
compositeType: TYPE_HOST_CATALOG_PLUGIN_AWS,
name: 'AWS',
credentialType: TYPE_CREDENTIAL_STATIC,
description: 'this is a Aws plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
// these are AWS fields and should be included
region: 'spain',
access_key_id: 'foobars',
secret_access_key: 'testing',
@@ -109,6 +203,7 @@ module('Unit | Serializer | host catalog', function (hooks) {
data: {
id: '1',
type: 'host-catalog',
credentialType: 'static',
attributes: {
type: 'plugin',
name: 'aws',
@@ -218,6 +218,17 @@ host-catalog:
aws: AWS
azure: Azure
plugin: Dynamic
credential:
label: Credential type
help: Select the credential type that you want to use.
static-credential:
edit: Static Credentials
label: Use an access key (Static Credentials)
help: Use an access key that you have generated in AWS to use with this AWS Host Catalog.
dynamic-credential:
edit: Dynamic Credentials
label: Use Assume Role (Dynamic Credentials)
help: Specify an Assume Role used to generate credentials to use with this AWS Host Catalog.
form:
aws_region:
label: AWS Region
@@ -19,12 +19,18 @@
{{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>
<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>{{error.message}}</E.Message>
<E.Message>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
@@ -38,21 +44,28 @@
disabled={{form.disabled}}
as |F|
>
<F.Label>{{t 'form.description.label'}}</F.Label>
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
<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>
<E.Message>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::Textarea::Field>
{{#if @model.isNew}}
<Hds::Form::RadioCard::Group @name='type' as |G|>
<G.Legend>{{t 'form.type.label'}}</G.Legend>
<G.Legend>
{{t 'form.type.label'}}
</G.Legend>
{{#each this.hostCatalogTypes as |hostCatalogType|}}
<G.RadioCard
@value={{hostCatalogType}}
@@ -61,19 +74,25 @@
{{on 'input' (fn @changeType hostCatalogType)}}
as |R|
>
<R.Label>{{t
(concat 'resources.host-catalog.types.' hostCatalogType)
}}</R.Label>
<R.Description>{{t
(concat 'resources.host-catalog.help.' hostCatalogType)
}}</R.Description>
<R.Label>
{{t (concat 'resources.host-catalog.types.' hostCatalogType)}}
</R.Label>
<R.Description>
{{t (concat 'resources.host-catalog.help.' hostCatalogType)}}
</R.Description>
</G.RadioCard>
{{/each}}
</Hds::Form::RadioCard::Group>
<Hds::Form::RadioCard::Group @name='plugin_type' @alignment='center' as |G|>
<G.Legend>{{t 'titles.provider'}}</G.Legend>
<G.Legend>{{t 'titles.choose-a-provider'}}</G.Legend>
<G.HelperText>{{t 'descriptions.choose-a-provider'}}</G.HelperText>
<G.Legend>
{{t 'titles.provider'}}
</G.Legend>
<G.Legend>
{{t 'titles.choose-a-provider'}}
</G.Legend>
<G.HelperText>
{{t 'descriptions.choose-a-provider'}}
</G.HelperText>
{{#each-in this.mapResourceTypeWithIcon as |pluginType icon|}}
<G.RadioCard
@@ -83,11 +102,10 @@
{{on 'input' (fn @changeType pluginType)}}
as |R|
>
<R.Label>{{t
(concat 'resources.host-catalog.types.' pluginType)
}}</R.Label>
<R.Label>
{{t (concat 'resources.host-catalog.types.' pluginType)}}
</R.Label>
<R.Icon @name={{icon}} />
</G.RadioCard>
{{/each-in}}
</Hds::Form::RadioCard::Group>
@@ -98,8 +116,11 @@
readonly={{true}}
as |F|
>
<F.Label>{{t 'titles.provider'}}</F.Label>
<F.HelperText>{{t 'descriptions.provider'}}
<F.Label>
{{t 'titles.provider'}}
</F.Label>
<F.HelperText>
{{t 'descriptions.provider'}}
</F.HelperText>
</InfoField>
{{/if}}
@@ -112,7 +133,9 @@
{{on 'input' (set-from-event @model 'region')}}
as |F|
>
<F.Label>{{t 'resources.host-catalog.form.aws_region.label'}}</F.Label>
<F.Label>
{{t 'resources.host-catalog.form.aws_region.label'}}
</F.Label>
<F.HelperText>
{{t 'resources.host-catalog.form.aws_region.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws.region'}}>
@@ -121,39 +144,225 @@
</F.HelperText>
</Hds::Form::TextInput::Field>
<Hds::Form::TextInput::Field
name='access_key_id'
@value={{@model.access_key_id}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'access_key_id')}}
as |F|
>
<F.Label>{{t 'form.access_key_id.label'}}</F.Label>
<F.HelperText>
{{t 'form.access_key_id.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
{{#if @model.isNew}}
<Hds::Form::Radio::Group @layout='vertical' @name='credential_type' as |G|>
<G.Legend>
{{t 'resources.host-catalog.types.credential.label'}}
</G.Legend>
<G.HelperText>
{{t 'resources.host-catalog.types.credential.help'}}
</G.HelperText>
<Hds::Form::TextInput::Field
name='secret_access_key'
@value={{@model.secret_access_key}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'secret_access_key')}}
as |F|
>
<F.Label>{{t 'form.secret_access_key.label'}}</F.Label>
<F.HelperText>
{{t 'form.secret_access_key.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
{{#each this.credentials as |credentialType|}}
<G.RadioField
name={{credentialType}}
@value={{credentialType}}
checked={{eq credentialType this.selectedCredentialType}}
disabled={{form.disabled}}
{{on 'input' (fn this.updateCredentialTypeSelection credentialType)}}
as |F|
>
<F.Label>
{{t
(concat
'resources.host-catalog.types.credential.'
credentialType
'.label'
)
}}
</F.Label>
<F.HelperText>
{{t
(concat
'resources.host-catalog.types.credential.'
credentialType
'.help'
)
}}
</F.HelperText>
</G.RadioField>
{{/each}}
</Hds::Form::Radio::Group>
{{else}}
<Hds::Form::Fieldset @layout='horizontal' as |F|>
<F.Legend>
{{t 'resources.host-catalog.types.credential.label'}}
</F.Legend>
<F.Control>
<Hds::Badge
@text={{t
(concat
'resources.host-catalog.types.credential.'
this.selectedCredentialType
'.edit'
)
}}
/>
</F.Control>
</Hds::Form::Fieldset>
{{/if}}
{{#if this.showDynamicCredentials}}
{{! role_arn }}
<Hds::Form::TextInput::Field
name='role_arn'
@isRequired={{true}}
@value={{@model.role_arn}}
@isInvalid={{@model.errors.role_arn}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'role_arn')}}
as |F|
>
<F.Label>
{{t 'resources.storage-bucket.form.role_arn.label'}}
</F.Label>
<F.HelperText>
{{t 'resources.storage-bucket.form.role_arn.help'}}
</F.HelperText>
{{#if @model.errors.role_arn}}
<F.Error as |E|>
{{#each @model.errors.role_arn as |error|}}
<E.Message>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::TextInput::Field>
{{! role_external_id }}
<Hds::Form::TextInput::Field
name='role_external_id'
@isOptional={{true}}
@value={{@model.role_external_id}}
@isInvalid={{@model.errors.role_external_id}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'role_external_id')}}
as |F|
>
<F.Label>
{{t 'resources.storage-bucket.form.role_external_id.label'}}
</F.Label>
<F.HelperText>
{{t 'resources.storage-bucket.form.role_external_id.help'}}
</F.HelperText>
{{#if @model.errors.role_external_id}}
<F.Error as |E|>
{{#each @model.errors.role_external_id as |error|}}
<E.Message>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::TextInput::Field>
{{! role_session_name }}
<Hds::Form::TextInput::Field
name='role_session_name'
@isOptional={{true}}
@value={{@model.role_session_name}}
@isInvalid={{@model.errors.role_session_name}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'role_session_name')}}
as |F|
>
<F.Label>
{{t 'resources.storage-bucket.form.role_session_name.label'}}
</F.Label>
<F.HelperText>
{{t 'resources.storage-bucket.form.role_session_name.help'}}
</F.HelperText>
{{#if @model.errors.role_session_name}}
<F.Error as |E|>
{{#each @model.errors.role_session_name as |error|}}
<E.Message>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::TextInput::Field>
{{! role_tags }}
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@disabled={{form.disabled}}
>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.storage-bucket.form.role_tags.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.storage-bucket.form.role_tags.help'}}
</F.HelperText>
{{#if @model.errors.role_tags}}
<F.Error as |E|>
{{#each @model.errors.role_tags as |error|}}
<E.Message>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.KeyValue
@name='role_tags'
@options={{@model.role_tags}}
@model={{@model}}
>
<:key as |K|>
<K.text />
</:key>
<:value as |V|>
<V.text />
</:value>
</F.KeyValue>
</:field>
</Form::Field::ListWrapper>
{{else}}
<Hds::Form::TextInput::Field
name='access_key_id'
@value={{@model.access_key_id}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'access_key_id')}}
as |F|
>
<F.Label>
{{t 'form.access_key_id.label'}}
</F.Label>
<F.HelperText>
{{t 'form.access_key_id.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<Hds::Form::TextInput::Field
name='secret_access_key'
@value={{@model.secret_access_key}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'secret_access_key')}}
as |F|
>
<F.Label>
{{t 'form.secret_access_key.label'}}
</F.Label>
<F.HelperText>
{{t 'form.secret_access_key.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
{{/if}}
<Hds::Form::Textarea::Field
name='worker_filter'
@@ -175,7 +384,9 @@
{{#if @model.errors.worker_filter}}
<F.Error as |E|>
{{#each @model.errors.worker_filter as |error|}}
<E.Message>{{error.message}}</E.Message>
<E.Message>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
@@ -188,7 +399,9 @@
{{on 'change' (fn this.toggleDisableCredentialRotation @model)}}
as |F|
>
<F.Label>{{t 'form.disable_credential_rotation.label'}}</F.Label>
<F.Label>
{{t 'form.disable_credential_rotation.label'}}
</F.Label>
</Hds::Form::Checkbox::Field>
&nbsp;
@@ -8,7 +8,10 @@ import { action } from '@ember/object';
import {
TYPES_HOST_CATALOG,
TYPES_HOST_CATALOG_PLUGIN,
TYPES_CREDENTIALS,
TYPE_CREDENTIAL_DYNAMIC,
} from 'api/models/host-catalog';
import { tracked } from '@glimmer/tracking';
//Note: this is a temporary solution till we have resource type helper in place
const icons = ['aws-color', 'azure-color'];
@@ -16,6 +19,21 @@ const icons = ['aws-color', 'azure-color'];
export default class FormHostCatalogAwsComponent extends Component {
// =properties
hostCatalogTypes = TYPES_HOST_CATALOG;
// =attributes
@tracked selectedCredentialType = this.args.model.credentialType;
@tracked showDynamicCredentials =
this.args.model.credentialType === TYPE_CREDENTIAL_DYNAMIC;
/**
* returns an array of available credential types
* @type {array}
*/
get credentials() {
return TYPES_CREDENTIALS;
}
/**
* maps resource type with icon
* @type {object}
@@ -32,4 +50,19 @@ export default class FormHostCatalogAwsComponent extends Component {
toggleDisableCredentialRotation(model) {
model.disable_credential_rotation = !model.disable_credential_rotation;
}
/**
* Allows to update the credential type
* @param type {string}
*/
@action
updateCredentialTypeSelection(type) {
this.selectedCredentialType = type;
if (type === TYPE_CREDENTIAL_DYNAMIC) {
this.showDynamicCredentials = true;
} else {
this.showDynamicCredentials = false;
}
this.args.changeCredentialType(type);
}
}
@@ -9,4 +9,5 @@
submit=@submit
cancel=@cancel
changeType=@changeType
changeCredentialType=@changeCredentialType
}}
@@ -424,7 +424,7 @@
{{t 'resources.storage-bucket.form.role_tags.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.storage-bucket.form.role_tags.label'}}
{{t 'resources.storage-bucket.form.role_tags.help'}}
</F.HelperText>
{{#if @model.errors.role_tags}}
@@ -436,11 +436,18 @@
{{/if}}
</:fieldset>
<:field as |F|>
<F.TextInput
<F.KeyValue
@name='role_tags'
@options={{@model.role_tags}}
@model={{@model}}
/>
>
<:key as |K|>
<K.text />
</:key>
<:value as |V|>
<V.text />
</:value>
</F.KeyValue>
</:field>
</Form::Field::ListWrapper>
{{/if}}
@@ -99,4 +99,14 @@ export default class ScopesScopeHostCatalogsIndexController extends Controller {
if (type === 'plugin') type = 'aws';
this.router.replaceWith({ queryParams: { type } });
}
/**
* Updates credential type
* @param {model} hostcatalog
* @param {string} credentialType
*/
@action
changeCredentialType(hostCatalog, credentialType) {
hostCatalog.credentialType = credentialType;
}
}
@@ -29,6 +29,7 @@
@submit={{fn this.hostCatalogs.save @model}}
@cancel={{fn this.hostCatalogs.cancel @model}}
@changeType={{this.hostCatalogs.changeType}}
@changeCredentialType={{fn this.hostCatalogs.changeCredentialType @model}}
/>
</page.body>