Workflow updates for Create Cluster
* Apply limitations to 'master_lb_enabled' checkbox interactions, such as master_count. * Basic validation for cluster name, to reduce create failures. * Rename 'Master' to 'Control Plane' in line with upstream Kubernetes. * Add validation for 'master_count': min/max and odd numbers. * 'Network' tab required indicator no longer shows when the 'create new network' checkbox is selected. This tab has no required options initially and the form can submit without visting the tab. * Help text updates to reflect the updated options. Change-Id: I7840ab606eb4a39fe34509d7b49d738391148bae
This commit is contained in:
parent
8680420c34
commit
c724172972
@ -1,5 +1,5 @@
|
||||
<h1 class="h4" translate>Additional Labels</h1>
|
||||
|
||||
<p translate>Specify additional kube_labels to apply to the cluster or override labels set by the cluster template. Overriding labels set by the cluster template may result in your cluster being misconfigured, unstable or unable to be created.</p>
|
||||
<p translate>Specify additional labels to apply to the cluster or override labels set by the cluster template. Overriding labels set by the cluster template may result in your cluster being misconfigured, unstable or unable to be created.</p>
|
||||
|
||||
<p translate>The key=value pair string is case insensitive and will be converted to lower case.</p>
|
||||
<p translate>The key=value pair string is case insensitive and will be converted to lower case.</p>
|
||||
|
@ -142,7 +142,7 @@
|
||||
templateResponse.flavor_id = 'ABC';
|
||||
|
||||
var model = $scope.model;
|
||||
model.cluster_template_id = '99'; // Triggers bussines logic revalidation
|
||||
model.cluster_template_id = '99'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
|
||||
expect(model.keypair).toBe(1);
|
||||
@ -168,7 +168,7 @@
|
||||
templateResponse.node_count = 1;
|
||||
templateResponse.flavor_id = 'ABC';
|
||||
|
||||
model.cluster_template_id = '99'; // Triggers bussines logic revalidation
|
||||
model.cluster_template_id = '99'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
|
||||
expect(model.keypair).toBe(99);
|
||||
@ -182,12 +182,12 @@
|
||||
'response contains negative `master_lb_enabled` flag', function() {
|
||||
$scope.model.master_count = 99;
|
||||
templateResponse.master_lb_enabled = false;
|
||||
$scope.model.cluster_template_id = '99'; // Triggers bussines logic revalidation
|
||||
$scope.model.cluster_template_id = '99'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
expect($scope.model.master_count).toBe(1);
|
||||
|
||||
$scope.model.master_count = MODEL_DEFAULTS.master_count;
|
||||
$scope.model.cluster_template_id = '999'; // Triggers bussines logic revalidation
|
||||
$scope.model.cluster_template_id = '999'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
expect($scope.model.master_count).toBe(1);
|
||||
});
|
||||
@ -196,7 +196,7 @@
|
||||
'template response', function() {
|
||||
templateResponse.labels = null;
|
||||
$scope.model.labels = MODEL_DEFAULTS.labels;
|
||||
$scope.model.cluster_template_id = '99'; // Triggers bussines logic revalidation
|
||||
$scope.model.cluster_template_id = '99'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
|
||||
expect($scope.model.labels).toEqual(MODEL_DEFAULTS.labels);
|
||||
@ -229,13 +229,13 @@
|
||||
|
||||
it('should not fail if the cluster template response is empty', function() {
|
||||
templateResponse = {};
|
||||
$scope.model.cluster_template_id = '99'; // Triggers bussines logic revalidation
|
||||
$scope.model.cluster_template_id = '99'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
it('should not fail if the cluster template\'s labels are empty', function() {
|
||||
templateResponse = {labels:{}};
|
||||
$scope.model.cluster_template_id = '99'; // Triggers bussines logic revalidation
|
||||
$scope.model.cluster_template_id = '99'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
@ -250,7 +250,7 @@
|
||||
|
||||
templateResponse.labels.ingress_controller = 'c2';
|
||||
|
||||
$scope.model.cluster_template_id = '99'; // Triggers bussines logic revalidation
|
||||
$scope.model.cluster_template_id = '99'; // Triggers business logic revalidation
|
||||
$scope.$apply();
|
||||
|
||||
expect($scope.model.ingress_controller.labels.ingress_controller).toBe('c2');
|
||||
|
@ -1,6 +1,11 @@
|
||||
<h1 class="h4" translate>Control Plane size</h1>
|
||||
|
||||
<p translate>Only uneven number of control plane nodes are supported. This provides the best balance of fault tolerance and cost for etcd.</p>
|
||||
|
||||
|
||||
<h1 class="h4" translate>Auto Scaling</h1>
|
||||
|
||||
<p translate>If enabled, the minimum and maximum number of worker nodes must be specified.</p>
|
||||
<p translate>Auto scaling requires the use of CPU and memory limits on the resource definition of Pods.</p>
|
||||
<p translate>If Kubernetes is unable to schedule a Pod due to insuficient CPU or memory in the cluster, a worker node will be added, as long as the maximum number of worker nodes has not been reached.</p>
|
||||
<p translate>If Kubernetes is unable to schedule a Pod due to insufficient CPU or memory in the cluster, a worker node will be added, as long as the maximum number of worker nodes has not been reached.</p>
|
||||
<p translate>If the aggregate resource limits of all existing Pods is lower than 50% of the cluster capacity, a worker node will be removed, as long as the minimum number of worker nodes has not been reached.</p>
|
||||
|
@ -44,6 +44,9 @@
|
||||
// comma-separated key=value with optional space after comma
|
||||
var REGEXP_KEY_VALUE = /^(\w+=[^,]+,?\s?)+$/;
|
||||
|
||||
// Object name, must start with alphabetical character.
|
||||
var REGEXP_CLUSTER_NAME = /^[a-zA-Z][a-zA-Z0-9_\-\.]*$/;
|
||||
|
||||
function ClusterWorkflow($q, basePath, gettext, magnum, neutron, nova) {
|
||||
var workflow = {
|
||||
init: init
|
||||
@ -59,9 +62,9 @@
|
||||
name: gettext('Choose an Availability Zone')}];
|
||||
var keypairsTitleMap = [{value: '', name: gettext('Choose a Keypair')}];
|
||||
var masterFlavorTitleMap = [{value: '',
|
||||
name: gettext('Choose a Flavor for the Master Node')}];
|
||||
name: gettext('Choose a Flavor for the Control Plane nodes')}];
|
||||
var workerFlavorTitleMap = [{value: '',
|
||||
name: gettext('Choose a Flavor for the Worker Node')}];
|
||||
name: gettext('Choose a Flavor for the Worker nodes')}];
|
||||
var networkTitleMap = [{value: '', name: gettext('Choose an existing network')}];
|
||||
var subnetTitleMap = [{value: '', name: fixedSubnetsInitial}];
|
||||
var ingressTitleMap = [{value: '', name: gettext('Choose an ingress controller')}];
|
||||
@ -86,7 +89,8 @@
|
||||
|
||||
'master_count': {
|
||||
type: 'number',
|
||||
minimum: 1
|
||||
minimum: 1,
|
||||
maximum: 7,
|
||||
},
|
||||
'master_flavor_id': { type: 'string' },
|
||||
'node_count': {
|
||||
@ -101,7 +105,7 @@
|
||||
},
|
||||
'max_node_count': { type: 'number' },
|
||||
|
||||
'master_lb_enabled': {type: 'boolean'},
|
||||
'master_lb_enabled': { type: 'boolean' },
|
||||
'create_network': { type: 'boolean' },
|
||||
'fixed_network': { type: 'string' },
|
||||
'fixed_subnet': { type: 'string' },
|
||||
@ -117,9 +121,17 @@
|
||||
|
||||
var formMasterCount = {
|
||||
key: 'master_count',
|
||||
title: gettext('Number of Master Nodes'),
|
||||
placeholder: gettext('The number of master nodes for the cluster'),
|
||||
required: true
|
||||
title: gettext('Number of Control Plane nodes'),
|
||||
placeholder: gettext('The number of Control Plane nodes for the cluster'),
|
||||
required: true,
|
||||
validationMessage: {
|
||||
'mustBeUnevenNumber': 'Supported control plane sizes are 1, 3, 5 or 7.'
|
||||
},
|
||||
$validators: {
|
||||
mustBeUnevenNumber: function(value) {
|
||||
return value % 2 !== 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Disable the Master Count field, if only a single master is allowed
|
||||
@ -151,7 +163,18 @@
|
||||
key: 'name',
|
||||
title: gettext('Cluster Name'),
|
||||
placeholder: gettext('Name of the cluster'),
|
||||
required: true
|
||||
required: true,
|
||||
help: "Text",
|
||||
validationMessage: {
|
||||
'invalidFormat': 'Cluster name must begin with an alphabetical ' +
|
||||
'character and only contain alphanumeric, underscore, ' +
|
||||
'dash and fullstop characters.'
|
||||
},
|
||||
$validators: {
|
||||
invalidFormat: function(value) {
|
||||
return REGEXP_CLUSTER_NAME.test(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'cluster_template_id',
|
||||
@ -203,7 +226,7 @@
|
||||
items: [
|
||||
{
|
||||
type: 'fieldset',
|
||||
title: gettext('Master Nodes'),
|
||||
title: gettext('Control Plane Nodes'),
|
||||
items: [
|
||||
formMasterCount,
|
||||
// Info message explaining why only single master node is enabled
|
||||
@ -211,14 +234,27 @@
|
||||
type: 'template',
|
||||
template: '<div class="alert alert-info">' +
|
||||
'<span class="fa fa-info-circle"></span> ' +
|
||||
gettext('The selected Cluster Template does not support ' +
|
||||
'multiple master nodes.') +
|
||||
gettext('The selected options do not support ' +
|
||||
'multiple control plane nodes. A Kubernetes ' +
|
||||
'API Load Balancer is required, and can be ' +
|
||||
'enabled in the Network tab.') +
|
||||
'</div>',
|
||||
condition: 'model.isSingleMasterNode == true'
|
||||
},
|
||||
// Info message explaining why we allow only uneven numbers of
|
||||
// control plane nodes.
|
||||
{
|
||||
type: 'template',
|
||||
template: '<div class="alert alert-info">' +
|
||||
'<span class="fa fa-info-circle"></span> ' +
|
||||
gettext('Only an uneven number of control plane nodes are allowed. ' +
|
||||
'This provides the best balance of fault tolerance and cost.') +
|
||||
'</div>',
|
||||
condition: 'false'
|
||||
},
|
||||
{
|
||||
key: 'master_flavor_id',
|
||||
title: gettext('Flavor of Master Nodes'),
|
||||
title: gettext('Flavor of Control Plane Nodes'),
|
||||
type: 'select',
|
||||
titleMap: masterFlavorTitleMap,
|
||||
required: true
|
||||
@ -304,7 +340,6 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -314,7 +349,7 @@
|
||||
help: basePath + 'clusters/workflow/network.help.html',
|
||||
type: 'section',
|
||||
htmlClass: 'row',
|
||||
required: true,
|
||||
required: false,
|
||||
items: [
|
||||
{
|
||||
type: 'section',
|
||||
@ -333,10 +368,11 @@
|
||||
key: 'create_network',
|
||||
title: gettext('Create New Network'),
|
||||
onChange: function(isNewNetwork) {
|
||||
if (isNewNetwork) {
|
||||
model.fixed_network = MODEL_DEFAULTS.fixed_network;
|
||||
model.fixed_subnet = MODEL_DEFAULTS.fixed_subnet;
|
||||
}
|
||||
// Reset relevant field selections
|
||||
model.fixed_network = MODEL_DEFAULTS.fixed_network;
|
||||
model.fixed_subnet = MODEL_DEFAULTS.fixed_subnet;
|
||||
// Network tab has required fields based on this checkbox.
|
||||
form[0].tabs[2].required = !isNewNetwork;
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -598,7 +634,7 @@
|
||||
|
||||
function changeFixedNetwork(model) {
|
||||
if (model.fixed_network) {
|
||||
subnetTitleMap = [{value:"", name: gettext("Choose an existing Subnet")}];
|
||||
subnetTitleMap = [{value: "", name: gettext("Choose an existing Subnet")}];
|
||||
angular.forEach(networkTitleMap, function(network) {
|
||||
if (network.value === model.fixed_network) {
|
||||
angular.forEach(network.subnets, function(subnet) {
|
||||
@ -607,10 +643,11 @@
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fixedSubnets = [{value:"", name: fixedSubnetsInitial}];
|
||||
model.fixed_subnet = "";
|
||||
fixedSubnets = [{value: "", name: fixedSubnetsInitial}];
|
||||
}
|
||||
// NOTE(dalees): This hardcoded index could be improved by referencing an object instead.
|
||||
form[0].tabs[2].items[0].items[0].items[3].titleMap = subnetTitleMap;
|
||||
model.fixed_subnet = MODEL_DEFAULTS.fixed_subnet;
|
||||
}
|
||||
|
||||
function onGetIngressControllers(response) {
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The Cluster Create workflow has been improved to provide better input
|
||||
validation.
|
||||
- |
|
||||
"Master" has been renamed to "Control Plane" in user facing locations
|
||||
in line with Kubernetes teminology. The Magnum API still uses old terms.
|
Loading…
Reference in New Issue
Block a user