ui: Add initial partition support to intentions (#11129)

* ui: Add initial partition support to intentions
This commit is contained in:
John Cowen 2021-09-24 17:31:58 +01:00 committed by GitHub
parent 78a3b9f3e1
commit b19e14e8a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 258 additions and 73 deletions

4
.changelog/11129.txt Normal file
View File

@ -0,0 +1,4 @@
```release-note:improvement
ui: Add initial support for partitions to intentions
```

View File

@ -7,10 +7,11 @@ import { get } from '@ember/object';
// will give us all the intentions that have the `ns` as either the SourceNS or
// the DestinationNS.
// We currently list intentions by the * wildcard namespace for back compat reasons
// FIXME: Is now a good time to change this behaviour ^ ?
// TODO: Update to use this.formatDatacenter()
export default class IntentionAdapter extends Adapter {
requestForQuery(request, { dc, ns, filter, index, uri }) {
requestForQuery(request, { dc, ns, partition, filter, index, uri }) {
return request`
GET /v1/connect/intentions?${{ dc }}
X-Request-ID: ${uri}${
@ -21,7 +22,7 @@ export default class IntentionAdapter extends Adapter {
}
${{
partition: '',
partition: '*',
ns: '*',
index,
filter,
@ -36,14 +37,21 @@ export default class IntentionAdapter extends Adapter {
// get the information we need from the id, which has been previously
// encoded
const [SourceNS, SourceName, DestinationNS, DestinationName] = id
.split(':')
.map(decodeURIComponent);
const [
SourcePartition,
SourceNS,
SourceName,
DestinationPartition,
DestinationNS,
DestinationName,
] = id.split(':').map(decodeURIComponent);
// FIXME: Service and Namespace are encoded into the URL here
// guessing we need to do the same thing for Partitions
return request`
GET /v1/connect/intentions/exact?${{
source: `${SourceNS}/${SourceName}`,
destination: `${DestinationNS}/${DestinationName}`,
source: `${SourcePartition}/${SourceNS}/${SourceName}`,
destination: `${DestinationPartition}/${DestinationNS}/${DestinationName}`,
dc: dc,
}}
Cache-Control: no-store
@ -54,10 +62,12 @@ export default class IntentionAdapter extends Adapter {
requestForCreateRecord(request, serialized, data) {
const body = {
SourceNS: serialized.SourceNS,
DestinationNS: serialized.DestinationNS,
SourceName: serialized.SourceName,
DestinationName: serialized.DestinationName,
SourceNS: serialized.SourceNS,
DestinationNS: serialized.DestinationNS,
SourcePartition: serialized.SourcePartition,
DestinationPartition: serialized.DestinationPartition,
SourceType: serialized.SourceType,
Meta: serialized.Meta,
Description: serialized.Description,
@ -72,10 +82,12 @@ export default class IntentionAdapter extends Adapter {
body.Permissions = serialized.Permissions;
}
}
// FIXME: Service and Namespace are encoded into the URL here
// guessing we need to do the same thing for Partitions
return request`
PUT /v1/connect/intentions/exact?${{
source: `${data.SourceNS}/${data.SourceName}`,
destination: `${data.DestinationNS}/${data.DestinationName}`,
source: `${data.SourcePartition}/${data.SourceNS}/${data.SourceName}`,
destination: `${data.DestinationPartition}/${data.DestinationNS}/${data.DestinationName}`,
dc: data.Datacenter,
}}
@ -85,16 +97,20 @@ export default class IntentionAdapter extends Adapter {
requestForUpdateRecord(request, serialized, data) {
// you can no longer save Destinations
delete serialized.DestinationNS;
delete serialized.DestinationName;
delete serialized.DestinationNS;
// FIXME: Does the above comment stand for partitions also?
delete serialized.DestinationPartition;
return this.requestForCreateRecord(...arguments);
}
requestForDeleteRecord(request, serialized, data) {
// FIXME: Service and Namespace are encoded into the URL here
// guessing we need to do the same thing for Partitions
return request`
DELETE /v1/connect/intentions/exact?${{
source: `${data.SourceNS}/${data.SourceName}`,
destination: `${data.DestinationNS}/${data.DestinationName}`,
source: `${data.SourcePartition}/${data.SourceNS}/${data.SourceName}`,
destination: `${data.DestinationPartition}/${data.DestinationNS}/${data.DestinationName}`,
dc: data.Datacenter,
}}
`;

View File

@ -28,7 +28,7 @@
<em>Search for an existing service, or enter any Service name.</em>
{{/if}}
</label>
{{#if (env 'CONSUL_NSPACES_ENABLED')}}
{{#if (can 'choose nspaces')}}
<label data-test-source-nspace class="type-select{{if item.error.SourceNS ' has-error'}}">
<span>Source Namespace</span>
<PowerSelectWithCreate
@ -50,7 +50,30 @@
<em>Search for an existing namespace, or enter any Namespace name.</em>
{{/if}}
</label>
{{/if}}
{{/if}}
{{#if (can 'choose partitions')}}
<label data-test-source-partition class="type-select{{if item.error.SourcePartition ' has-error'}}">
<span>Source Partition</span>
<PowerSelectWithCreate
@disabled={{not create}}
@options={{partitions}}
@selected={{SourcePartition}}
@searchPlaceholder="Type partition name"
@buildSuggestion={{action "createNewLabel" "Use a Consul Partition called '{{term}}'"}}
@showCreateWhen={{action "isUnique" partitions}}
@onCreate={{action onchange "SourcePartition"}}
@onChange={{action onchange "SourcePartition"}} as |partition|>
{{#if (eq partition.Name '*') }}
* (All Partitions)
{{else}}
{{partition.Name}}
{{/if}}
</PowerSelectWithCreate>
{{#if create}}
<em>Search for an existing partition, or enter any Partition name.</em>
{{/if}}
</label>
{{/if}}
</fieldset>
<fieldset>
<h2>Destination</h2>
@ -76,7 +99,7 @@
<em>Search for an existing service, or enter any Service name.</em>
{{/if}}
</label>
{{#if (env 'CONSUL_NSPACES_ENABLED')}}
{{#if (can 'choose nspaces')}}
<label data-test-destination-nspace class="type-select{{if item.error.DestinationNS ' has-error'}}">
<span>Destination Namespace</span>
<PowerSelectWithCreate
@ -99,7 +122,31 @@
<em>For the destination, you may choose any namespace for which you have access.</em>
{{/if}}
</label>
{{/if}}
{{/if}}
{{#if (can 'choose partitions')}}
<label data-test-destination-partition class="type-select{{if item.error.DestinationPartition ' has-error'}}">
<span>Destination Partition</span>
<PowerSelectWithCreate
@disabled={{not create}}
@options={{partitions}}
@searchField="Name"
@selected={{DestinationPartition}}
@searchPlaceholder="Type partition name"
@buildSuggestion={{action "createNewLabel" "Use a future Consul Partition called '{{term}}'"}}
@showCreateWhen={{action "isUnique" partitions}}
@onCreate={{action onchange "DestinationPartition"}}
@onChange={{action onchange "DestinationPartition"}} as |partition|>
{{#if (eq partition.Name '*') }}
* (All Partitions)
{{else}}
{{partition.Name}}
{{/if}}
</PowerSelectWithCreate>
{{#if create}}
<em>For the destination, you may choose any partition for which you have access.</em>
{{/if}}
</label>
{{/if}}
</fieldset>
</div>
<fieldset>

View File

@ -95,6 +95,17 @@ as |api|>
/>
{{/if}}
{{#if (can 'use partitions')}}
<DataSource
@src={{uri '/*/*/${dc}/partitions'
(hash
dc=@dc
)
}}
@onchange={{action this.createPartitions item}}
/>
{{/if}}
{{#if (and api.isCreate this.isManagedByCRDs)}}
<Consul::Intention::Notice::CustomResource @type="warning" />
{{/if}}
@ -103,11 +114,14 @@ as |api|>
>
<Consul::Intention::Form::Fieldsets
@nspaces={{this.nspaces}}
@partitions={{this.partitions}}
@services={{this.services}}
@SourceName={{this.SourceName}}
@SourceNS={{this.SourceNS}}
@SourcePartition={{this.SourcePartition}}
@DestinationName={{this.DestinationName}}
@DestinationNS={{this.DestinationNS}}
@DestinationPartition={{this.DestinationPartition}}
@item={{item}}
@disabled={{api.disabled}}
@create={{api.isCreate}}

View File

@ -12,6 +12,10 @@ export default class ConsulIntentionForm extends Component {
@tracked SourceNS;
@tracked DestinationNS;
@tracked partitions;
@tracked SourcePartition;
@tracked DestinationPartition;
@tracked isManagedByCRDs;
modal = null; // reference to the warning modal
@ -115,6 +119,28 @@ export default class ConsulIntentionForm extends Component {
this.DestinationNS = destination;
}
@action
createPartitions(item, e) {
// Partitions in the menus should:
// 1. Include an 'All Partitions' option
// 2. Include the current SourcePartition and DestinationPartition incase they don't exist yet
let items = e.data.toArray().sort((a, b) => a.Name.localeCompare(b.Name));
items = [{ Name: '*' }].concat(items);
let source = items.findBy('Name', item.SourcePartition);
if (!source) {
source = { Name: item.SourcePartition };
items = [source].concat(items);
}
let destination = items.findBy('Name', item.DestinationPartition);
if (!destination) {
destination = { Name: item.DestinationPartition };
items = [destination].concat(items);
}
this.partitions = items;
this.SourcePartition = source;
this.DestinationPartition = destination;
}
@action
change(e, form, item) {
const target = e.target;
@ -125,6 +151,8 @@ export default class ConsulIntentionForm extends Component {
case 'DestinationName':
case 'SourceNS':
case 'DestinationNS':
case 'SourcePartition':
case 'DestinationPartition':
name = selected = target.value;
// Names can be selected Service EmberObjects or typed in strings
// if its not a string, use the `Name` from the Service EmberObject
@ -158,6 +186,13 @@ export default class ConsulIntentionForm extends Component {
this.nspaces = [selected].concat(this.nspaces.toArray());
}
break;
case 'SourcePartition':
case 'DestinationPartition':
if (this.partitions.filterBy('Name', name).length === 0) {
selected = { Name: name };
this.partitions = [selected].concat(this.partitions.toArray());
}
break;
}
this[target.name] = selected;
break;

View File

@ -1,5 +1,26 @@
.consul-intention-list {
td.permissions {
%consul-intention-list td.permissions {
color: $blue-500;
}
}
%consul-intention-list em {
--word-spacing: 0.25rem;
}
%consul-intention-list em span::before,
%consul-intention-list em span:first-child {
margin-right: var(--word-spacing);
}
%consul-intention-list em span:last-child {
margin-left: var(--word-spacing);
}
%consul-intention-list em span::before {
@extend %as-pseudo;
}
%consul-intention-list span[class|='nspace']::before {
@extend %with-folder-outline-mask;
}
%consul-intention-list span[class|='partition']::before {
@extend %with-user-team-mask;
}
.consul-intention-list {
@extend %consul-intention-list;
}

View File

@ -25,7 +25,13 @@ as |item index|>
{{item.SourceName}}
{{/if}}
{{! TODO: slugify }}
<em class={{concat 'nspace-' (or item.SourceNS 'default')}}>{{or item.SourceNS 'default'}}</em>
<em>
<span
class={{concat 'partition-' (or item.SourcePartition 'default')}}
>{{or item.SourcePartition 'default'}}</span> / <span
class={{concat 'nspace-' (or item.SourceNS 'default')}}
>{{or item.SourceNS 'default'}}</span>
</em>
</a>
</td>
<td class="intent intent-{{slugify item.Action}}" data-test-intention-action={{item.Action}}>
@ -39,7 +45,13 @@ as |item index|>
{{item.DestinationName}}
{{/if}}
{{! TODO: slugify }}
<em class={{concat 'nspace-' (or item.DestinationNS 'default')}}>{{or item.DestinationNS 'default'}}</em>
<em>
<span
class={{concat 'partition-' (or item.DestinationPartition 'default')}}
>{{or item.DestinationPartition 'default'}}</span> / <span
class={{concat 'nspace-' (or item.DestinationNS 'default')}}
>{{or item.DestinationNS 'default'}}</span>
</em>
</span>
</td>
<td class="permissions">

View File

@ -4,11 +4,11 @@
<dl>
<dt>Destination</dt>
<dd>
{{item.DestinationName}}{{#if (env "CONSUL_NSPACES_ENABLED")}} / {{item.DestinationNS}}{{/if}}
{{item.DestinationName}}{{#if (can "use partitions")}} / {{item.DestinationPartition}}{{/if}}{{#if (can "use nspaces")}} / {{item.DestinationNS}}{{/if}}
</dd>
<dt>Source</dt>
<dd>
{{item.SourceName}}{{#if (env "CONSUL_NSPACES_ENABLED")}} / {{item.SourceNS}}{{/if}}
{{item.SourceName}}{{#if (can "use partitions")}} / {{item.SourcePartition}}{{/if}}{{#if (can "use nspaces")}} / {{item.SourceNS}}{{/if}}
</dd>
{{#if item.Action}}
<dt>Action</dt>

View File

@ -12,11 +12,14 @@ export default class Intention extends Model {
@attr('string') Datacenter;
@attr('string') Description;
// FIXME: Will we have Source/DestinationPartition?
@attr('string', { defaultValue: () => 'default' }) SourceNS;
@attr('string', { defaultValue: () => '*' }) SourceName;
@attr('string', { defaultValue: () => 'default' }) DestinationNS;
@attr('string', { defaultValue: () => '*' }) DestinationName;
@attr('string', { defaultValue: () => 'default' }) SourceNS;
@attr('string', { defaultValue: () => 'default' }) DestinationNS;
@attr('string', { defaultValue: () => 'default' }) SourcePartition;
@attr('string', { defaultValue: () => 'default' }) DestinationPartition;
@attr('number') Precedence;
@attr('string', { defaultValue: () => 'consul' }) SourceType;
@nullValue(undefined) @attr('string') Action;

View File

@ -22,7 +22,7 @@ export default class IntentionSerializer extends Serializer {
item.LegacyID = item.ID;
}
item.ID = this
.uri`${item.SourceNS}:${item.SourceName}:${item.DestinationNS}:${item.DestinationName}`;
.uri`${item.SourcePartition}:${item.SourceNS}:${item.SourceName}:${item.DestinationPartition}:${item.DestinationNS}:${item.DestinationName}`;
return item;
}
@ -56,7 +56,7 @@ export default class IntentionSerializer extends Serializer {
return respond((headers, body) => {
body = data;
body.ID = this
.uri`${serialized.SourceNS}:${serialized.SourceName}:${serialized.DestinationNS}:${serialized.DestinationName}`;
.uri`${serialized.SourcePartition}:${serialized.SourceNS}:${serialized.SourceName}:${serialized.DestinationPartition}:${serialized.DestinationNS}:${serialized.DestinationName}`;
return this.fingerprint(primaryKey, slugKey, body.Datacenter)(body);
});
}

View File

@ -69,9 +69,12 @@ export default class IntentionRepository extends RepositoryService {
let item;
if (params.id === '') {
const defaultNspace = this.env.var('CONSUL_NSPACES_ENABLED') ? '*' : 'default';
const defaultPartition = this.env.var('CONSUL_PARTITIONS_ENABLED') ? '*' : 'default';
item = await this.create({
SourceNS: params.nspace || defaultNspace,
DestinationNS: params.nspace || defaultNspace,
SourcePartition: params.partition || defaultPartition,
DestinationPartition: params.partition || defaultPartition,
Datacenter: params.dc,
Partition: params.partition,
});

View File

@ -84,6 +84,9 @@ main,
html:not(.has-nspaces) [class*='nspace-'] {
display: none;
}
html:not(.has-partitions) [class*='partition-'] {
display: none;
}
#wrapper {
@extend %viewport-container;
display: flex;

View File

@ -22,10 +22,12 @@ ${legacy ? `
"Action": "${fake.helpers.randomize(['allow', 'deny'])}",
`:``}
"Description": "${fake.lorem.sentence()}",
"SourceNS": "default",
"SourceName": "${fake.hacker.noun()}-${i}",
"DestinationNS": "default",
"DestinationName": "${fake.hacker.noun()}",
"SourceNS": "default",
"DestinationNS": "default",
"SourcePartition": "default",
"DestinationPartition": "default",
"SourceType": "${fake.helpers.randomize(['consul', 'externaluri'])}",
${!legacy ? `
"Permissions": [

View File

@ -6,10 +6,12 @@ return `
"ID": "${legacy ? ID : ''}"
${ http.method !== "PUT" ? `
,"Description": "${fake.lorem.sentence()}",
"SourceNS": "default",
"SourceName": "${fake.hacker.noun()}",
"DestinationNS": "default",
"DestinationName": "${fake.hacker.noun()}",
"SourceNS": "default",
"DestinationNS": "default",
"SourcePartition": "default",
"DestinationPartition": "default",
"SourceType": "${fake.helpers.randomize(['consul', 'externaluri'])}",
${legacy ? `
"Action": "${fake.helpers.randomize(['allow', 'deny'])}",

View File

@ -8,10 +8,12 @@ return `
"ID": "${legacy ? ID : ''}"
${ http.method !== "PUT" ? `
,"Description": "${fake.lorem.sentence()}",
"SourceNS": "${source[0]}",
"SourceName": "${source[1]}",
"DestinationNS": "${destination[0]}",
"DestinationName": "${destination[1]}",
"SourceName": "${source[2]}",
"DestinationName": "${destination[2]}",
"SourceNS": "${source[1]}",
"DestinationNS": "${destination[1]}",
"SourcePartition": "${source[0]}",
"DestinationPartition": "${destination[0]}",
"SourceType": "${fake.helpers.randomize(['consul', 'externaluri'])}",
${legacy ? `
"Action": "${fake.helpers.randomize(['allow', 'deny'])}",

View File

@ -46,13 +46,15 @@ Feature: dc / intentions / create: Intention Create
# Specifically set deny
And I click ".value-deny"
And I submit
Then a PUT request was made to "/v1/connect/intentions/exact?source=nspace-0%2Fweb&destination=nspace-0%2Fdb&dc=datacenter" from yaml
Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fnspace-0%2Fweb&destination=default%2Fnspace-0%2Fdb&dc=datacenter" from yaml
---
body:
SourceName: web
DestinationName: db
SourceNS: nspace-0
DestinationNS: nspace-0
SourcePartition: default
DestinationPartition: default
Action: deny
---
Then the url should be /datacenter/intentions
@ -90,7 +92,7 @@ Feature: dc / intentions / create: Intention Create
# Specifically set deny
And I click ".value-deny"
And I submit
Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fweb&destination=default%2Fdb&dc=datacenter" from yaml
Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fdefault%2Fweb&destination=default%2Fdefault%2Fdb&dc=datacenter" from yaml
---
body:
SourceName: web

View File

@ -4,10 +4,12 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
Given 1 datacenter model with the value "datacenter"
And 1 intention model from yaml
---
SourceNS: default
SourceName: name
DestinationNS: default
DestinationName: destination
SourceNS: default
DestinationNS: default
SourcePartition: default
DestinationPartition: default
ID: ee52203d-989f-4f7a-ab5a-2bef004164ca
Meta: ~
---
@ -19,7 +21,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
And I click actions on the intentionList.intentions
And I click delete on the intentionList.intentions
And I click confirmDelete on the intentionList.intentions
Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter"
Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fdefault%2Fname&destination=default%2Fdefault%2Fdestination&dc=datacenter"
And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class
Scenario: Deleting an intention from the intention detail page
@ -30,7 +32,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
---
And I click delete
And I click confirmDelete
Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter"
Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fdefault%2Fname&destination=default%2Fdefault%2Fdestination&dc=datacenter"
And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class
Scenario: Deleting an intention from the intention detail page and getting an error
@ -39,7 +41,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
dc: datacenter
intention: ee52203d-989f-4f7a-ab5a-2bef004164ca
---
Given the url "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter" responds with a 500 status
Given the url "/v1/connect/intentions/exact?source=default%2Fdefault%2Fname&destination=default%2Fdefault%2Fdestination&dc=datacenter" responds with a 500 status
And I click delete
And I click confirmDelete
And "[data-notification]" has the "notification-update" class
@ -50,7 +52,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
dc: datacenter
intention: ee52203d-989f-4f7a-ab5a-2bef004164ca
---
Given the url "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter" responds with from yaml
Given the url "/v1/connect/intentions/exact?source=default%2Fdefault%2Fname&destination=default%2Fdefault%2Fdestination&dc=datacenter" responds with from yaml
---
status: 500
body: "duplicate intention found:"

View File

@ -4,10 +4,12 @@ Feature: dc / intentions / permissions / warn: Intention Permission Warn
Given 1 datacenter model with the value "datacenter"
And 1 intention model from yaml
---
SourceNS: default
SourceName: web
DestinationNS: default
DestinationName: db
SourceNS: default
DestinationNS: default
SourcePartition: default
DestinationPartition: default
Action: ~
Permissions:
- Action: allow
@ -28,4 +30,4 @@ Feature: dc / intentions / permissions / warn: Intention Permission Warn
And I submit
And I see the warning object
And I click the warning.confirm object
Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fweb&destination=default%2Fdb&dc=datacenter" from yaml
Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fdefault%2Fweb&destination=default%2Fdefault%2Fdb&dc=datacenter" from yaml

View File

@ -4,10 +4,12 @@ Feature: dc / intentions / update: Intention Update
Given 1 datacenter model with the value "datacenter"
And 1 intention model from yaml
---
SourceNS: default
SourceName: web
DestinationNS: default
DestinationName: db
SourceNS: default
DestinationNS: default
SourcePartition: default
DestinationPartition: default
ID: intention-id
---
When I visit the intention page for yaml
@ -24,7 +26,7 @@ Feature: dc / intentions / update: Intention Update
---
And I click "[value=[Action]]"
And I submit
Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fweb&destination=default%2Fdb&dc=datacenter" from yaml
Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fdefault%2Fweb&destination=default%2Fdefault%2Fdb&dc=datacenter" from yaml
---
Description: [Description]
Action: [Action]
@ -39,7 +41,7 @@ Feature: dc / intentions / update: Intention Update
| Desc | allow |
------------------------------
Scenario: There was an error saving the intention
Given the url "/v1/connect/intentions/exact?source=default%2Fweb&destination=default%2Fdb&dc=datacenter" responds with a 500 status
Given the url "/v1/connect/intentions/exact?source=default%2Fdefault%2Fweb&destination=default%2Fdefault%2Fdb&dc=datacenter" responds with a 500 status
And I submit
Then the url should be /datacenter/intentions/intention-id
Then "[data-notification]" has the "notification-update" class

View File

@ -15,10 +15,12 @@ Feature: dc / services / show / intentions: Intentions per service
- ID: 755b72bd-f5ab-4c92-90cc-bed0e7d8e9f0
Action: allow
Meta: ~
SourceNS: default
SourceName: name
DestinationNS: default
DestinationName: destination
SourceNS: default
DestinationNS: default
SourcePartition: default
DestinationPartition: default
- ID: 755b72bd-f5ab-4c92-90cc-bed0e7d8e9f1
Action: deny
@ -39,11 +41,11 @@ Feature: dc / services / show / intentions: Intentions per service
Scenario: I can see intentions
And I see 3 intention models on the intentionList component
And I click intention on the intentionList.intentions component
Then the url should be /dc1/services/service-0/intentions/default:name:default:destination
Then the url should be /dc1/services/service-0/intentions/default:default:name:default:default:destination
Scenario: I can delete intentions
And I click actions on the intentionList.intentions component
And I click delete on the intentionList.intentions component
And I click confirmDelete on the intentionList.intentions
Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=dc1"
Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fdefault%2Fname&destination=default%2Fdefault%2Fdestination&dc=dc1"
And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class

View File

@ -7,7 +7,8 @@ const nspaceRunner = getNspaceRunner('intention');
module('Integration | Adapter | intention', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'SourceNS:SourceName:DestinationNS:DestinationName';
const id =
'SourcePartition:SourceNS:SourceName:DestinationPartition:DestinationNS:DestinationName';
test('requestForQuery returns the correct url', function(assert) {
return nspaceRunner(
(adapter, serializer, client) => {
@ -15,6 +16,7 @@ module('Integration | Adapter | intention', function(hooks) {
return adapter.requestForQuery(request, {
dc: dc,
ns: 'team-1',
partition: 'partition-1',
filter: '*',
index: 1,
});
@ -23,6 +25,7 @@ module('Integration | Adapter | intention', function(hooks) {
filter: '*',
index: 1,
ns: '*',
partition: '*',
},
{
filter: '*',
@ -36,7 +39,7 @@ module('Integration | Adapter | intention', function(hooks) {
const adapter = this.owner.lookup('adapter:intention');
const client = this.owner.lookup('service:client/http');
const request = client.url.bind(client);
const expected = `GET /v1/connect/intentions/exact?source=SourceNS%2FSourceName&destination=DestinationNS%2FDestinationName&dc=${dc}`;
const expected = `GET /v1/connect/intentions/exact?source=SourcePartition%2FSourceNS%2FSourceName&destination=DestinationPartition%2FDestinationNS%2FDestinationName&dc=${dc}`;
const actual = adapter
.requestForQueryRecord(request, {
dc: dc,
@ -59,17 +62,19 @@ module('Integration | Adapter | intention', function(hooks) {
const adapter = this.owner.lookup('adapter:intention');
const client = this.owner.lookup('service:client/http');
const request = client.url.bind(client);
const expected = `PUT /v1/connect/intentions/exact?source=SourceNS%2FSourceName&destination=DestinationNS%2FDestinationName&dc=${dc}`;
const expected = `PUT /v1/connect/intentions/exact?source=SourcePartition%2FSourceNS%2FSourceName&destination=DestinationPartition%2FDestinationNS%2FDestinationName&dc=${dc}`;
const actual = adapter
.requestForCreateRecord(
request,
{},
{
Datacenter: dc,
SourceNS: 'SourceNS',
SourceName: 'SourceName',
DestinationNS: 'DestinationNS',
DestinationName: 'DestinationName',
SourceNS: 'SourceNS',
DestinationNS: 'DestinationNS',
SourcePartition: 'SourcePartition',
DestinationPartition: 'DestinationPartition',
}
)
.split('\n')[0];
@ -79,17 +84,19 @@ module('Integration | Adapter | intention', function(hooks) {
const adapter = this.owner.lookup('adapter:intention');
const client = this.owner.lookup('service:client/http');
const request = client.url.bind(client);
const expected = `PUT /v1/connect/intentions/exact?source=SourceNS%2FSourceName&destination=DestinationNS%2FDestinationName&dc=${dc}`;
const expected = `PUT /v1/connect/intentions/exact?source=SourcePartition%2FSourceNS%2FSourceName&destination=DestinationPartition%2FDestinationNS%2FDestinationName&dc=${dc}`;
const actual = adapter
.requestForUpdateRecord(
request,
{},
{
Datacenter: dc,
SourceNS: 'SourceNS',
SourceName: 'SourceName',
DestinationNS: 'DestinationNS',
DestinationName: 'DestinationName',
SourceNS: 'SourceNS',
DestinationNS: 'DestinationNS',
SourcePartition: 'SourcePartition',
DestinationPartition: 'DestinationPartition',
}
)
.split('\n')[0];
@ -99,17 +106,19 @@ module('Integration | Adapter | intention', function(hooks) {
const adapter = this.owner.lookup('adapter:intention');
const client = this.owner.lookup('service:client/http');
const request = client.url.bind(client);
const expected = `DELETE /v1/connect/intentions/exact?source=SourceNS%2FSourceName&destination=DestinationNS%2FDestinationName&dc=${dc}`;
const expected = `DELETE /v1/connect/intentions/exact?source=SourcePartition%2FSourceNS%2FSourceName&destination=DestinationPartition%2FDestinationNS%2FDestinationName&dc=${dc}`;
const actual = adapter
.requestForDeleteRecord(
request,
{},
{
Datacenter: dc,
SourceNS: 'SourceNS',
SourceName: 'SourceName',
DestinationNS: 'DestinationNS',
DestinationName: 'DestinationName',
SourceNS: 'SourceNS',
DestinationNS: 'DestinationNS',
SourcePartition: 'SourcePartition',
DestinationPartition: 'DestinationPartition',
}
)
.split('\n')[0];

View File

@ -26,7 +26,7 @@ module('Integration | Serializer | intention', function(hooks) {
// refactored out our Serializer this can go
Namespace: nspace,
Partition: partition,
uid: `["${partition}","${nspace}","${dc}","${item.SourceNS}:${item.SourceName}:${item.DestinationNS}:${item.DestinationName}"]`,
uid: `["${partition}","${nspace}","${dc}","${item.SourcePartition}:${item.SourceNS}:${item.SourceName}:${item.DestinationPartition}:${item.DestinationNS}:${item.DestinationName}"]`,
})
);
const actual = serializer.respondForQuery(
@ -55,10 +55,12 @@ module('Integration | Serializer | intention', function(hooks) {
url: `/v1/connect/intentions/${id}?dc=${dc}`,
};
const item = {
SourceNS: 'SourceNS',
SourceName: 'SourceName',
DestinationNS: 'DestinationNS',
DestinationName: 'DestinationName',
SourceNS: 'SourceNS',
DestinationNS: 'DestinationNS',
SourcePartition: 'SourcePartition',
DestinationPartition: 'DestinationPartition',
};
return get(request.url).then(function(payload) {
payload = {
@ -76,7 +78,7 @@ module('Integration | Serializer | intention', function(hooks) {
// refactored out our Serializer this can go
Namespace: nspace,
Partition: partition,
uid: `["${partition}","${nspace}","${dc}","${item.SourceNS}:${item.SourceName}:${item.DestinationNS}:${item.DestinationName}"]`,
uid: `["${partition}","${nspace}","${dc}","${item.SourcePartition}:${item.SourceNS}:${item.SourceName}:${item.DestinationPartition}:${item.DestinationNS}:${item.DestinationName}"]`,
});
const actual = serializer.respondForQueryRecord(
function(cb) {