diff --git a/ui/app/styles/core/columns.scss b/ui/app/styles/core/columns.scss
index d68425e9a88..d912a37c31c 100644
--- a/ui/app/styles/core/columns.scss
+++ b/ui/app/styles/core/columns.scss
@@ -14,4 +14,13 @@
&.is-bottom-aligned {
align-items: flex-end;
}
+
+ &.is-max-half {
+ max-width: 50%;
+ }
+
+ &.is-centered {
+ margin-left: auto;
+ margin-right: auto;
+ }
}
diff --git a/ui/app/templates/csi/plugins/index.hbs b/ui/app/templates/csi/plugins/index.hbs
index 7d4f5f1f1f9..2eab1929145 100644
--- a/ui/app/templates/csi/plugins/index.hbs
+++ b/ui/app/templates/csi/plugins/index.hbs
@@ -42,8 +42,12 @@
{{#link-to "csi.plugins.plugin" row.model.plainId class="is-primary"}}{{row.model.plainId}}{{/link-to}}
- {{if (gt row.model.controllersHealthy 0) "Healthy" "Unhealthy"}}
- ({{row.model.controllersHealthy}}/{{row.model.controllersExpected}})
+ {{#if row.model.controllerRequired}}
+ {{if (gt row.model.controllersHealthy 0) "Healthy" "Unhealthy"}}
+ ({{row.model.controllersHealthy}}/{{row.model.controllersExpected}})
+ {{else}}
+ Node Only
+ {{/if}}
|
{{if (gt row.model.nodesHealthy 0) "Healthy" "Unhealthy"}}
diff --git a/ui/app/templates/csi/plugins/plugin.hbs b/ui/app/templates/csi/plugins/plugin.hbs
index bcd2c8873de..7c11e183784 100644
--- a/ui/app/templates/csi/plugins/plugin.hbs
+++ b/ui/app/templates/csi/plugins/plugin.hbs
@@ -5,11 +5,13 @@
Plugin Details
-
- Controller Health
- {{format-percentage model.controllersHealthy total=model.controllersExpected}}
- ({{model.controllersHealthy}}/{{model.controllersExpected}})
-
+ {{#if model.controllerRequired}}
+
+ Controller Health
+ {{format-percentage model.controllersHealthy total=model.controllersExpected}}
+ ({{model.controllersHealthy}}/{{model.controllersExpected}})
+
+ {{/if}}
Node Health
{{format-percentage model.nodesHealthy total=model.nodesExpected}}
@@ -23,38 +25,40 @@
-
-
- Controller Health
-
-
-
- {{gauge-chart
- label="Availability"
- value=model.controllersHealthy
- total=model.controllersExpected}}
-
-
-
- Available
- {{model.controllersHealthy}}
+ {{#if model.controllerRequired}}
+
+
+ Controller Health
+
+
+
+ {{gauge-chart
+ label="Availability"
+ value=model.controllersHealthy
+ total=model.controllersExpected}}
-
-
-
- Expected
- {{model.controllersExpected}}
+
+
+ Available
+ {{model.controllersHealthy}}
+
+
+
+
+ Expected
+ {{model.controllersExpected}}
+
-
+ {{/if}}
-
+
Node Health
-
+
{{gauge-chart
label="Availability"
@@ -79,42 +83,44 @@
-
-
- Controller Allocations
-
-
- {{#if model.controllers}}
- {{#list-table
- source=sortedControllers
- class="with-foot" as |t|}}
- {{#t.head}}
- |
- ID |
- Created |
- Modified |
- Health |
- Client |
- Job |
- Version |
- Volumes |
- CPU |
- Memory |
- {{/t.head}}
- {{#t.body key="model.allocID" as |row|}}
- {{plugin-allocation-row
- data-test-controller-allocation=row.model.allocID
- pluginAllocation=row.model}}
- {{/t.body}}
- {{/list-table}}
- {{else}}
-
- No Controller Plugin Allocations
- No allocations are providing controller plugin service.
-
- {{/if}}
+ {{#if model.controllerRequired}}
+
+
+ Controller Allocations
+
+
+ {{#if model.controllers}}
+ {{#list-table
+ source=sortedControllers
+ class="with-foot" as |t|}}
+ {{#t.head}}
+ |
+ ID |
+ Created |
+ Modified |
+ Health |
+ Client |
+ Job |
+ Version |
+ Volumes |
+ CPU |
+ Memory |
+ {{/t.head}}
+ {{#t.body key="model.allocID" as |row|}}
+ {{plugin-allocation-row
+ data-test-controller-allocation=row.model.allocID
+ pluginAllocation=row.model}}
+ {{/t.body}}
+ {{/list-table}}
+ {{else}}
+
+ No Controller Plugin Allocations
+ No allocations are providing controller plugin service.
+
+ {{/if}}
+
-
+ {{/if}}
diff --git a/ui/mirage/factories/csi-plugin.js b/ui/mirage/factories/csi-plugin.js
index b0f95354532..6e462267993 100644
--- a/ui/mirage/factories/csi-plugin.js
+++ b/ui/mirage/factories/csi-plugin.js
@@ -11,10 +11,19 @@ export default Factory.extend({
provider: faker.helpers.randomize(STORAGE_PROVIDERS),
version: '1.0.1',
+
controllerRequired: faker.random.boolean,
- controllersHealthy: () => faker.random.number(3),
+
+ controllersHealthy() {
+ if (!this.controllerRequired) return 0;
+ return faker.random.number(3);
+ },
controllersExpected() {
- return this.controllersHealthy + faker.random.number({ min: 1, max: 2 });
+ // This property must be read before the conditional
+ // or Mirage will incorrectly sort dependent properties.
+ const healthy = this.controllersHealthy;
+ if (!this.controllerRequired) return 0;
+ return healthy + faker.random.number({ min: 1, max: 2 });
},
nodesHealthy: () => faker.random.number(3),
@@ -25,7 +34,10 @@ export default Factory.extend({
// Internal property to determine whether or not this plugin
// Should create one or two Jobs to represent Node and
// Controller plugins.
- isMonolith: faker.random.boolean,
+ isMonolith() {
+ if (!this.controllerRequired) return false;
+ return faker.random.boolean;
+ },
// When false, the plugin will not make its own volumes
createVolumes: true,
@@ -49,11 +61,13 @@ export default Factory.extend({
shallow: plugin.shallow,
});
} else {
- const controllerJob = server.create('job', {
- type: 'service',
- createAllocations: false,
- shallow: plugin.shallow,
- });
+ const controllerJob =
+ plugin.controllerRequired &&
+ server.create('job', {
+ type: 'service',
+ createAllocations: false,
+ shallow: plugin.shallow,
+ });
const nodeJob = server.create('job', {
type: 'service',
createAllocations: false,
@@ -63,10 +77,12 @@ export default Factory.extend({
job: nodeJob,
shallow: plugin.shallow,
});
- storageControllers = server.createList('storage-controller', plugin.controllersExpected, {
- job: controllerJob,
- shallow: plugin.shallow,
- });
+ storageControllers =
+ plugin.controllerRequired &&
+ server.createList('storage-controller', plugin.controllersExpected, {
+ job: controllerJob,
+ shallow: plugin.shallow,
+ });
}
plugin.update({
diff --git a/ui/tests/acceptance/plugin-detail-test.js b/ui/tests/acceptance/plugin-detail-test.js
index b97cdfb8a9b..d2403ad0591 100644
--- a/ui/tests/acceptance/plugin-detail-test.js
+++ b/ui/tests/acceptance/plugin-detail-test.js
@@ -14,7 +14,7 @@ module('Acceptance | plugin detail', function(hooks) {
hooks.beforeEach(function() {
server.create('node');
- plugin = server.create('csi-plugin');
+ plugin = server.create('csi-plugin', { controllerRequired: true });
});
test('/csi/plugins/:id should have a breadcrumb trail linking back to Plugins and Storage', async function(assert) {
@@ -147,6 +147,7 @@ module('Acceptance | plugin detail', function(hooks) {
test('when there are no plugin allocations, the tables present empty states', async function(assert) {
const emptyPlugin = server.create('csi-plugin', {
+ controllerRequired: true,
controllersHealthy: 0,
controllersExpected: 0,
nodesHealthy: 0,
@@ -161,4 +162,16 @@ module('Acceptance | plugin detail', function(hooks) {
assert.ok(PluginDetail.nodeTableIsEmpty);
assert.equal(PluginDetail.nodeEmptyState.headline, 'No Node Plugin Allocations');
});
+
+ test('when the plugin is node-only, the controller information is omitted', async function(assert) {
+ const nodeOnlyPlugin = server.create('csi-plugin', { controllerRequired: false });
+
+ await PluginDetail.visit({ id: nodeOnlyPlugin.id });
+
+ assert.notOk(PluginDetail.controllerAvailabilityIsPresent);
+ assert.ok(PluginDetail.nodeAvailabilityIsPresent);
+
+ assert.notOk(PluginDetail.controllerHealthIsPresent);
+ assert.notOk(PluginDetail.controllerTableIsPresent);
+ });
});
diff --git a/ui/tests/acceptance/plugins-list-test.js b/ui/tests/acceptance/plugins-list-test.js
index f86e2d66120..22710fa2b69 100644
--- a/ui/tests/acceptance/plugins-list-test.js
+++ b/ui/tests/acceptance/plugins-list-test.js
@@ -35,7 +35,7 @@ module('Acceptance | plugins list', function(hooks) {
});
test('each plugin row should contain information about the plugin', async function(assert) {
- const plugin = server.create('csi-plugin', { shallow: true });
+ const plugin = server.create('csi-plugin', { shallow: true, controllerRequired: true });
await PluginsList.visit();
@@ -55,6 +55,23 @@ module('Acceptance | plugins list', function(hooks) {
assert.equal(pluginRow.provider, plugin.provider);
});
+ test('node only plugins explain that there is no controller health for this plugin type', async function(assert) {
+ const plugin = server.create('csi-plugin', { shallow: true, controllerRequired: false });
+
+ await PluginsList.visit();
+
+ const pluginRow = PluginsList.plugins.objectAt(0);
+ const nodeHealthStr = plugin.nodesHealthy > 0 ? 'Healthy' : 'Unhealthy';
+
+ assert.equal(pluginRow.id, plugin.id);
+ assert.equal(pluginRow.controllerHealth, 'Node Only');
+ assert.equal(
+ pluginRow.nodeHealth,
+ `${nodeHealthStr} (${plugin.nodesHealthy}/${plugin.nodesExpected})`
+ );
+ assert.equal(pluginRow.provider, plugin.provider);
+ });
+
test('each plugin row should link to the corresponding plugin', async function(assert) {
const plugin = server.create('csi-plugin', { shallow: true });
diff --git a/ui/tests/integration/plugin-allocation-row-test.js b/ui/tests/integration/plugin-allocation-row-test.js
index e11aee8016f..b85d2d492b4 100644
--- a/ui/tests/integration/plugin-allocation-row-test.js
+++ b/ui/tests/integration/plugin-allocation-row-test.js
@@ -20,7 +20,7 @@ module('Integration | Component | plugin allocation row', function(hooks) {
});
test('Plugin allocation row immediately fetches the plugin allocation', async function(assert) {
- const plugin = this.server.create('csi-plugin', { id: 'plugin' });
+ const plugin = this.server.create('csi-plugin', { id: 'plugin', controllerRequired: true });
const storageController = plugin.controllers.models[0];
const pluginRecord = await this.store.find('plugin', 'csi/plugin');
@@ -42,7 +42,7 @@ module('Integration | Component | plugin allocation row', function(hooks) {
});
test('After the plugin allocation row fetches the plugin allocation, allocation stats are fetched', async function(assert) {
- const plugin = this.server.create('csi-plugin', { id: 'plugin' });
+ const plugin = this.server.create('csi-plugin', { id: 'plugin', controllerRequired: true });
const storageController = plugin.controllers.models[0];
const pluginRecord = await this.store.find('plugin', 'csi/plugin');
@@ -66,6 +66,7 @@ module('Integration | Component | plugin allocation row', function(hooks) {
const plugin = this.server.create('csi-plugin', {
id: 'plugin',
isMonolith: false,
+ controllerRequired: true,
controllersExpected: 2,
});
const storageController = plugin.controllers.models[0];
diff --git a/ui/tests/pages/storage/plugins/detail.js b/ui/tests/pages/storage/plugins/detail.js
index 2c148d0e696..1191c0be3ff 100644
--- a/ui/tests/pages/storage/plugins/detail.js
+++ b/ui/tests/pages/storage/plugins/detail.js
@@ -15,10 +15,14 @@ export default create({
title: text('[data-test-title]'),
+ controllerHealthIsPresent: isPresent('[data-test-plugin-controller-health]'),
controllerHealth: text('[data-test-plugin-controller-health]'),
nodeHealth: text('[data-test-plugin-node-health]'),
provider: text('[data-test-plugin-provider]'),
+ controllerAvailabilityIsPresent: isPresent('[data-test-plugin-controller-availability]'),
+ nodeAvailabilityIsPresent: isPresent('[data-test-plugin-node-availability]'),
+
breadcrumbs: collection('[data-test-breadcrumb]', {
id: attribute('data-test-breadcrumb'),
text: text(),
@@ -32,6 +36,8 @@ export default create({
...allocations('[data-test-controller-allocation]', 'controllerAllocations'),
...allocations('[data-test-node-allocation]', 'nodeAllocations'),
+ controllerTableIsPresent: isPresent('[data-test-controller-allocations]'),
+
controllerTableIsEmpty: isPresent('[data-test-empty-controller-allocations]'),
controllerEmptyState: {
headline: text('[data-test-empty-controller-allocations-headline]'),
|