diff --git a/.changelog/9432.txt b/.changelog/9432.txt new file mode 100644 index 000000000..dfbf12e98 --- /dev/null +++ b/.changelog/9432.txt @@ -0,0 +1,4 @@ +```release-note:bug +ui: request intention listing with ns=* parameter to retrieve all intentions +across namespaces +``` diff --git a/ui/packages/consul-ui/app/adapters/intention.js b/ui/packages/consul-ui/app/adapters/intention.js index 4b047589c..f39f10209 100644 --- a/ui/packages/consul-ui/app/adapters/intention.js +++ b/ui/packages/consul-ui/app/adapters/intention.js @@ -2,12 +2,16 @@ import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './applica import { get } from '@ember/object'; import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc'; -// Intentions use SourceNS and DestinationNS properties for namespacing so we -// don't need to add the `?ns=` anywhere here +// Intentions have different namespacing to the rest of the UI in that the don't +// have a Namespace property, the DestinationNS is essentially its namespace. +// Listing of intentions still requires the `ns` query string parameter which +// 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 // TODO: Update to use this.formatDatacenter() export default class IntentionAdapter extends Adapter { - requestForQuery(request, { dc, filter, index, uri }) { + requestForQuery(request, { dc, ns, filter, index, uri }) { return request` GET /v1/connect/intentions?${{ dc }} X-Request-ID: ${uri}${ @@ -18,6 +22,7 @@ export default class IntentionAdapter extends Adapter { } ${{ + ...this.formatNspace('*'), index, filter, }} diff --git a/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs b/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs index 129189c4b..788faccc5 100644 --- a/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs @@ -72,7 +72,7 @@ as |api|> {{/if}} {{#if (env 'CONSUL_NSPACES_ENABLED')}} diff --git a/ui/packages/consul-ui/app/routes/dc/intentions/edit.js b/ui/packages/consul-ui/app/routes/dc/intentions/edit.js index c151cbf5b..36aa0fd6e 100644 --- a/ui/packages/consul-ui/app/routes/dc/intentions/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/intentions/edit.js @@ -1,24 +1,30 @@ import { inject as service } from '@ember/service'; import Route from 'consul-ui/routing/route'; -import { hash } from 'rsvp'; export default class EditRoute extends Route { - @service('repository/intention') - repo; + @service('repository/intention') repo; + @service('env') env; - model({ intention_id }, transition) { + async model({ intention_id }, transition) { const dc = this.modelFor('dc').dc.Name; - const nspace = '*'; - return hash({ - dc: dc, - nspace: nspace, - item: - typeof intention_id !== 'undefined' - ? this.repo.findBySlug(intention_id, dc, nspace) - : this.repo.create({ - Datacenter: dc, - }), - }); + const nspace = this.modelFor('nspace').nspace.substr(1); + + let item; + if (typeof intention_id !== 'undefined') { + item = await this.repo.findBySlug(intention_id, dc, nspace); + } else { + const defaultNspace = this.env.var('CONSUL_NSPACES_ENABLED') ? '*' : 'default'; + item = await this.repo.create({ + SourceNS: nspace || defaultNspace, + DestinationNS: nspace || defaultNspace, + Datacenter: dc, + }); + } + return { + dc, + nspace, + item, + }; } setupController(controller, model) { diff --git a/ui/packages/consul-ui/app/routes/dc/intentions/index.js b/ui/packages/consul-ui/app/routes/dc/intentions/index.js index 0e94d4aac..65479ab4b 100644 --- a/ui/packages/consul-ui/app/routes/dc/intentions/index.js +++ b/ui/packages/consul-ui/app/routes/dc/intentions/index.js @@ -17,7 +17,7 @@ export default class IndexRoute extends Route { model(params) { return { dc: this.modelFor('dc').dc.Name, - nspace: this.modelFor('nspace').nspace.substr(1) || 'default', + nspace: this.modelFor('nspace').nspace.substr(1), }; } diff --git a/ui/packages/consul-ui/app/services/repository.js b/ui/packages/consul-ui/app/services/repository.js index a332e8325..82cb346cc 100644 --- a/ui/packages/consul-ui/app/services/repository.js +++ b/ui/packages/consul-ui/app/services/repository.js @@ -30,7 +30,7 @@ export default class RepositoryService extends Service { if (dc === meta.dc) { if (checkNspace) { const nspace = get(item, 'Namespace'); - if (nspace !== meta.namespace) { + if (typeof nspace !== 'undefined' && nspace !== meta.nspace) { return; } } diff --git a/ui/packages/consul-ui/tests/acceptance/dc/intentions/create.feature b/ui/packages/consul-ui/tests/acceptance/dc/intentions/create.feature index ab46d9613..273aecac8 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/intentions/create.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/intentions/create.feature @@ -3,7 +3,51 @@ Feature: dc / intentions / create: Intention Create In order to define intentions As a user I want to visit the intention create page, fill in the form and hit the create button and see a success notification - Scenario: + @onlyNamespaceable + Scenario: with namespaces enabled + Given 1 datacenter model with the value "datacenter" + And 3 service models from yaml + --- + - Name: web + Kind: ~ + - Name: db + Kind: ~ + - Name: cache + Kind: ~ + --- + When I visit the intention page for yaml + --- + dc: datacenter + --- + Then the url should be /datacenter/intentions/create + And the title should be "New Intention - Consul" + # Set source + And I click "[data-test-source-element] .ember-power-select-trigger" + And I type "web" into ".ember-power-select-search-input" + And I click ".ember-power-select-option:first-child" + Then I see the text "web" in "[data-test-source-element] .ember-power-select-selected-item" + # Set destination + And I click "[data-test-destination-element] .ember-power-select-trigger" + And I type "db" into ".ember-power-select-search-input" + And I click ".ember-power-select-option:first-child" + Then I see the text "db" in "[data-test-destination-element] .ember-power-select-selected-item" + # Specifically set deny + And I click "[value=deny]" + And I submit + # TODO: When namespace is empty we expect * + # Then a PUT request was made to "/v1/connect/intentions/exact?source=@namespace%2Fweb&destination=@namespace%2Fdb&dc=datacenter" from yaml + # --- + # body: + # SourceName: web + # DestinationName: db + # Action: deny + # --- + Then the url should be /datacenter/intentions + And the title should be "Intentions - Consul" + And "[data-notification]" has the "notification-update" class + And "[data-notification]" has the "success" class + @notNamespaceable + Scenario: with namespaces disabled Given 1 datacenter model with the value "datacenter" And 3 service models from yaml --- diff --git a/ui/packages/consul-ui/tests/integration/adapters/intention-test.js b/ui/packages/consul-ui/tests/integration/adapters/intention-test.js index fa8ceea4d..19b567df4 100644 --- a/ui/packages/consul-ui/tests/integration/adapters/intention-test.js +++ b/ui/packages/consul-ui/tests/integration/adapters/intention-test.js @@ -1,17 +1,35 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; +import getNspaceRunner from 'consul-ui/tests/helpers/get-nspace-runner'; + +const nspaceRunner = getNspaceRunner('intention'); + module('Integration | Adapter | intention', function(hooks) { setupTest(hooks); const dc = 'dc-1'; const id = 'SourceNS:SourceName:DestinationNS:DestinationName'; test('requestForQuery returns the correct url', function(assert) { - const adapter = this.owner.lookup('adapter:intention'); - const client = this.owner.lookup('service:client/http'); - const expected = `GET /v1/connect/intentions?dc=${dc}`; - const actual = adapter.requestForQuery(client.requestParams.bind(client), { - dc: dc, - }); - assert.equal(`${actual.method} ${actual.url}`, expected); + return nspaceRunner( + (adapter, serializer, client) => { + return adapter.requestForQuery(client.body, { + dc: dc, + ns: 'team-1', + filter: '*', + index: 1, + }); + }, + { + filter: '*', + index: 1, + ns: '*', + }, + { + filter: '*', + index: 1, + }, + this, + assert + ); }); test('requestForQueryRecord returns the correct url', function(assert) { const adapter = this.owner.lookup('adapter:intention'); diff --git a/ui/packages/consul-ui/tests/steps.js b/ui/packages/consul-ui/tests/steps.js index 1d61f0fb3..08866ecbc 100644 --- a/ui/packages/consul-ui/tests/steps.js +++ b/ui/packages/consul-ui/tests/steps.js @@ -102,7 +102,7 @@ export default function({ visit(library, pages, utils.setCurrentPage, reset); click(library, utils.find, helpers.click); form(library, utils.find, helpers.fillIn, helpers.triggerKeyEvent, utils.getCurrentPage); - debug(library, assert, utils.currentURL); + debug(library, assert, helpers.currentURL); assertHttp(library, assert, lastNthRequest); assertModel(library, assert, utils.find, utils.getCurrentPage, pauseUntil, pluralize); assertPage(library, assert, utils.find, utils.getCurrentPage, $);