diff --git a/app/models/cluster.js b/app/models/cluster.js
index 9d72f5d5d5..7596698de8 100644
--- a/app/models/cluster.js
+++ b/app/models/cluster.js
@@ -336,6 +336,10 @@ export default Resource.extend(Grafana, ResourceUsage, {
.filter((s) => !(s.conditions || []).any((c) => c.status === 'True'));
}),
+ masterNodes: computed('nodes.@each.{state,labels}', function() {
+ return (this.nodes || []).filter((node) => node.labels && node.labels[C.NODES.MASTER_NODE]);
+ }),
+
inactiveNodes: computed('nodes.@each.state', function() {
return (get(this, 'nodes') || []).filter( (n) => C.ACTIVEISH_STATES.indexOf(get(n, 'state')) === -1 );
}),
diff --git a/app/models/node.js b/app/models/node.js
index 57cce9c042..210e060f00 100644
--- a/app/models/node.js
+++ b/app/models/node.js
@@ -158,6 +158,36 @@ var Node = Resource.extend(Grafana, StateCounts, ResourceUsage, {
return taints.some((taint) => UNSCHEDULABLE_KEYS.includes(taint.key) && UNSCHEDULABLE_EFFECTS.includes(taint.effect));
}),
+ isK3sNode: computed('labels', function() {
+ const labels = get(this, 'labels') || {};
+
+ return Object.prototype.hasOwnProperty.call(labels, C.LABEL.NODE_INSTANCE_TYPE);
+ }),
+
+ k3sNodeArgs: computed('annotations', function() {
+ const { annotations } = this;
+ const nodeArgs = annotations[C.LABEL.K3S_NODE_ARGS] ? JSON.parse(annotations[C.LABEL.K3S_NODE_ARGS]) : [];
+
+ return nodeArgs.join(' ');
+ }),
+
+ k3sNodeEnvVar: computed('annotations', function() {
+ const { annotations } = this;
+ const nodeEnv = annotations[C.LABEL.K3S_NODE_ENV] ? JSON.parse(annotations[C.LABEL.K3S_NODE_ENV]) : {};
+ const nodeEnvArr = [];
+
+ Object.keys(nodeEnv).forEach((envKey) => {
+ const out = {
+ key: envKey,
+ value: nodeEnv[envKey]
+ };
+
+ nodeEnvArr.push(out)
+ })
+
+ return nodeEnvArr;
+ }),
+
osBlurb: computed('info.os.operatingSystem', function() {
var out = get(this, 'info.os.operatingSystem') || '';
diff --git a/lib/monitoring/addon/components/node-dashboard/component.js b/lib/monitoring/addon/components/node-dashboard/component.js
index 0fd0a730d1..6d6cf42ab4 100644
--- a/lib/monitoring/addon/components/node-dashboard/component.js
+++ b/lib/monitoring/addon/components/node-dashboard/component.js
@@ -4,10 +4,13 @@ import { inject as service } from '@ember/service';
import layout from './template';
export default Component.extend({
- scope: service(),
+ scope: service(),
layout,
- model: null,
+ model: null,
+ sortBy: 'key',
+ descending: false,
+ isK3sNode: alias('model.node.isK3sNode'),
monitoringEnabled: alias('scope.currentCluster.enableClusterMonitoring'),
isMonitoringReady: alias('scope.currentCluster.isMonitoringReady'),
});
diff --git a/lib/monitoring/addon/components/node-dashboard/template.hbs b/lib/monitoring/addon/components/node-dashboard/template.hbs
index 6dc4d809f4..678d35c6f6 100644
--- a/lib/monitoring/addon/components/node-dashboard/template.hbs
+++ b/lib/monitoring/addon/components/node-dashboard/template.hbs
@@ -68,6 +68,22 @@
expandAll=al.expandAll
expandFn=expandFn
}}
+ {{#if isK3sNode}}
+
{{system-info-section
node=model.node
diff --git a/lib/shared/addon/components/annotations-section/component.js b/lib/shared/addon/components/annotations-section/component.js
index cbb3984ca6..b13039593a 100644
--- a/lib/shared/addon/components/annotations-section/component.js
+++ b/lib/shared/addon/components/annotations-section/component.js
@@ -3,6 +3,13 @@ import { alias } from '@ember/object/computed';
import Component from '@ember/component';
import ManageLabels from 'shared/mixins/manage-labels';
import layout from './template';
+import C from 'ui/utils/constants';
+
+const K3S_LABELS_TO_IGNORE = [
+ C.LABEL.K3S_NODE_ARGS,
+ C.LABEL.K3S_NODE_CONFIG_HASH,
+ C.LABEL.K3S_NODE_ENV
+];
export default Component.extend(ManageLabels, {
layout,
@@ -25,11 +32,13 @@ export default Component.extend(ManageLabels, {
],
annotationSource: alias('model.annotations'),
+
didReceiveAttrs() {
- this.initLabels(this.get('annotationSource'));
+ this.initLabels(this.get('annotationSource'), null, null, null, K3S_LABELS_TO_IGNORE);
},
+
annotationsObserver: observer('model.annotations', function() {
- this.initLabels(this.get('annotationSource'));
+ this.initLabels(this.get('annotationSource'), null, null, null, K3S_LABELS_TO_IGNORE);
}),
});
diff --git a/lib/shared/addon/components/cluster-driver/driver-import/component.js b/lib/shared/addon/components/cluster-driver/driver-import/component.js
index 975f36f2dc..8827d1ce63 100644
--- a/lib/shared/addon/components/cluster-driver/driver-import/component.js
+++ b/lib/shared/addon/components/cluster-driver/driver-import/component.js
@@ -1,7 +1,7 @@
import Component from '@ember/component'
import ClusterDriver from 'shared/mixins/cluster-driver';
import { inject as service } from '@ember/service';
-import { get, set, observer } from '@ember/object';
+import { computed, get, set, observer } from '@ember/object';
import { alias, equal } from '@ember/object/computed';
import layout from './template';
@@ -12,13 +12,15 @@ export default Component.extend(ClusterDriver, {
settings: service(),
layout,
- configField: 'importedConfig',
- step: 1,
- loading: false,
- clusterAdmin: CLUSTER_ADMIN,
+ configField: 'importedConfig',
+ step: 1,
+ loading: false,
+ nodeForInfo: null,
+ clusterAdmin: CLUSTER_ADMIN,
isEdit: equal('mode', 'edit'),
clusterState: alias('model.originalCluster.state'),
+ isK3sCluster: equal('model.cluster.driver', 'k3s'),
didReceiveAttrs() {
if ( get(this, 'isEdit') &&
@@ -26,6 +28,19 @@ export default Component.extend(ClusterDriver, {
) {
this.loadToken();
}
+
+ if ( get(this, 'isEdit') && this.isK3sCluster && this.model.cluster.masterNodes.length === 1 ) {
+ set(this, 'nodeForInfo', this.model.cluster.masterNodes.firstObject);
+ }
+ },
+
+ actions: {
+ driverUpdateK3sCluster() {
+ },
+
+ setActiveNodeForInfo(nodeId) {
+ set(this, 'nodeForInfo', this.nodes.findBy('id', nodeId));
+ },
},
clusterChanged: observer('originalCluster.state', function() {
@@ -40,6 +55,17 @@ export default Component.extend(ClusterDriver, {
}
}),
+ nodes: computed('model.cluster.masterNodes.@each.{state}', function() {
+ return this.model.cluster.masterNodes;
+ }),
+
+ nodesOptions: computed('nodes.@each.{state}', function() {
+ return this.nodes.map((node) => ( {
+ id: node.id,
+ displayName: node.displayName
+ } ));
+ }),
+
doneSaving() {
if ( get(this, 'isEdit') ) {
if (this.close) {
diff --git a/lib/shared/addon/components/cluster-driver/driver-import/template.hbs b/lib/shared/addon/components/cluster-driver/driver-import/template.hbs
index b05fd86198..be957543ab 100644
--- a/lib/shared/addon/components/cluster-driver/driver-import/template.hbs
+++ b/lib/shared/addon/components/cluster-driver/driver-import/template.hbs
@@ -1,5 +1,66 @@
-{{#if (and (eq step 2) (eq originalCluster.state "pending"))}}
+{{#if (and (eq step 1) isK3sCluster)}}
+
+
+
+
+
+
+
+
+ {{t "k3sClusterInfo.nodeInfo.label"}}
+
+ {{new-select
+ id="master-node-select"
+ classNames="form-control"
+ optionValuePath="id"
+ optionLabelPath="displayName"
+ content=nodesOptions
+ value=nodeForInfo.id
+ action=(action "setActiveNodeForInfo")
+ prompt="k3sClusterInfo.nodeInfo.selectPrompt"
+ localizedPrompt=true
+ }}
+
+ {{#if nodeForInfo}}
+
+
+
+
+ {{/if}}
+
+
+
+
+
+
+
+
+{{else if (and (eq step 2) (eq originalCluster.state "pending"))}}
{{#banner-message color="bg-info m-0"}}
{{t "clusterNew.import.command.instructionsAdminRole"
diff --git a/lib/shared/addon/components/cru-cluster/component.js b/lib/shared/addon/components/cru-cluster/component.js
index f6103701ea..ae6a6d0e8b 100644
--- a/lib/shared/addon/components/cru-cluster/component.js
+++ b/lib/shared/addon/components/cru-cluster/component.js
@@ -41,6 +41,7 @@ export default Component.extend(ViewNewEdit, ChildHook, {
primaryResource: alias('model.cluster'),
isCustom: equal('driverInfo.nodeWhich', 'custom'),
+ isK3sCluster: equal('model.cluster.driver', 'k3s'),
init() {
this._super(...arguments);
@@ -252,6 +253,12 @@ export default Component.extend(ViewNewEdit, ChildHook, {
preSave: true
});
+ out.push({
+ name: 'k3s',
+ driver: 'import',
+ preSave: true
+ });
+
out.forEach((driver) => {
const key = `clusterNew.${ driver.name }.label`;
diff --git a/lib/shared/addon/components/cru-cluster/template.hbs b/lib/shared/addon/components/cru-cluster/template.hbs
index 1c1a9a087f..893e33df16 100644
--- a/lib/shared/addon/components/cru-cluster/template.hbs
+++ b/lib/shared/addon/components/cru-cluster/template.hbs
@@ -65,7 +65,7 @@
+
+
+
Kubernetes Version
+
+ {{#input-or-display
+ editable=editing
+ value=k3sConfig.kubernetesVersion.gitVersion
+ }}
+ {{input
+ id="node-kube-version"
+ class="form-control"
+ type="text"
+ value=k3sConfig.kubernetesVersion.gitVersion
+ }}
+ {{/input-or-display}}
+
+
+
+
+
+
+ {{t "k3sClusterInfo.serverConcurrency"}}
+
+
+ {{#input-or-display
+ editable=editing
+ value=k3sConfig.serverConcurrency
+ }}
+ {{input
+ id="node-server-concurrency"
+ class="form-control"
+ type="number"
+ value=k3sConfig.serverConcurrency
+ }}
+ {{/input-or-display}}
+
+
+
+
+ {{t "k3sClusterInfo.workerConcurrency"}}
+
+
+ {{#input-or-display
+ editable=editing
+ value=k3sConfig.workerConcurrency
+ }}
+ {{input
+ id="node-worker-concurrency"
+ class="form-control"
+ type="number"
+ value=k3sConfig.workerConcurrency
+ }}
+ {{/input-or-display}}
+
+
+
\ No newline at end of file
diff --git a/lib/shared/addon/components/k3s-node-args/component.js b/lib/shared/addon/components/k3s-node-args/component.js
new file mode 100644
index 0000000000..9849028325
--- /dev/null
+++ b/lib/shared/addon/components/k3s-node-args/component.js
@@ -0,0 +1,10 @@
+import Component from '@ember/component';
+import layout from './template';
+
+export default Component.extend({
+ layout,
+
+ classNames: ['col', 'span-12', 'box'],
+
+ node: null,
+});
diff --git a/lib/shared/addon/components/k3s-node-args/template.hbs b/lib/shared/addon/components/k3s-node-args/template.hbs
new file mode 100644
index 0000000000..a7d262df63
--- /dev/null
+++ b/lib/shared/addon/components/k3s-node-args/template.hbs
@@ -0,0 +1,13 @@
+
+ {{t "clusterDashboard.k3sInfo.nodeArgs.title"}}
+
+
+ {{t "clusterDashboard.k3sInfo.nodeArgs.detail"}}
+
+{{#if (gte node.k3sNodeArgs.length 1)}}
+
{{node.k3sNodeArgs}}
+{{else}}
+
+ {{t "clusterDashboard.k3sInfo.nodeArgs.noArgs"}}
+
+{{/if}}
\ No newline at end of file
diff --git a/lib/shared/addon/components/k3s-node-env-var/component.js b/lib/shared/addon/components/k3s-node-env-var/component.js
new file mode 100644
index 0000000000..cb43e75708
--- /dev/null
+++ b/lib/shared/addon/components/k3s-node-env-var/component.js
@@ -0,0 +1,24 @@
+import Component from '@ember/component';
+import layout from './template';
+
+const HEADERS = [
+ {
+ name: 'key',
+ sort: ['key'],
+ translationKey: 'annotationsSection.key',
+ },
+ {
+ name: 'value',
+ sort: ['value', 'key'],
+ translationKey: 'annotationsSection.value',
+ },
+];
+
+export default Component.extend({
+ layout,
+ classNames: ['col', 'span-12', 'box'],
+
+ node: null,
+ headers: HEADERS,
+
+});
diff --git a/lib/shared/addon/components/k3s-node-env-var/template.hbs b/lib/shared/addon/components/k3s-node-env-var/template.hbs
new file mode 100644
index 0000000000..487a09bcb3
--- /dev/null
+++ b/lib/shared/addon/components/k3s-node-env-var/template.hbs
@@ -0,0 +1,42 @@
+
+ {{t "k3sNodeEnvVarSection.title"}}
+
+
+ {{t "k3sNodeEnvVarSection.detail"}}
+
+{{#sortable-table
+ classNames="grid fixed mb-0 sortable-table"
+ bulkActions=false
+ rowActions=false
+ paging=false
+ search=true
+ sortBy=sortBy
+ stickyHeader=false
+ descending=descending
+ headers=headers
+ body=node.k3sNodeEnvVar
+ as |sortable kind label|
+}}
+ {{#if (eq kind "row")}}
+
+
+ {{label.key}}
+
+
+ {{pretty-json value=label.value}}
+
+
+ {{else if (eq kind "norows")}}
+
+
+ {{t "k3sNodeEnvVarSection.noData"}}
+
+
+ {{else if (eq kind "nomatch")}}
+
+
+ {{t "k3sNodeEnvVarSection.noMatch"}}
+
+
+ {{/if}}
+{{/sortable-table}}
\ No newline at end of file
diff --git a/lib/shared/addon/components/project-member-row/template.hbs b/lib/shared/addon/components/project-member-row/template.hbs
index f26692acc4..bcb4016054 100644
--- a/lib/shared/addon/components/project-member-row/template.hbs
+++ b/lib/shared/addon/components/project-member-row/template.hbs
@@ -47,9 +47,9 @@
- {{#unless isCreatorMember}}
+ {{#if (and (not isCreatorMember) editing)}}
- {{/unless}}
+ {{/if}}
\ No newline at end of file
diff --git a/lib/shared/addon/mixins/manage-labels.js b/lib/shared/addon/mixins/manage-labels.js
index 535c9f561f..1614a28a98 100644
--- a/lib/shared/addon/mixins/manage-labels.js
+++ b/lib/shared/addon/mixins/manage-labels.js
@@ -3,6 +3,7 @@ import EmberObject, { set, setProperties, computed } from '@ember/object';
import Mixin from '@ember/object/mixin';
import C from 'ui/utils/constants';
import { debouncedObserver } from 'ui/utils/debounce';
+import { isEmpty } from '@ember/utils';
const USER = 'user';
const SYSTEM = 'system';
@@ -243,8 +244,13 @@ export default Mixin.create({
}
},
- initLabels(obj, onlyOfType, onlyKeys, readonlyKeys) {
+ initLabels(obj, onlyOfType, onlyKeys, readonlyKeys, labelsToIgnore) {
let out = [];
+ let ignoredLabels = [...C.LABELS_TO_IGNORE];
+
+ if (!isEmpty(labelsToIgnore)) {
+ ignoredLabels.pushObjects(labelsToIgnore);
+ }
if ( onlyKeys && !isArray(onlyKeys) ) {
onlyKeys = [onlyKeys];
@@ -263,7 +269,7 @@ export default Mixin.create({
type = 'system';
}
- if ( C.LABELS_TO_IGNORE.indexOf(key) >= 0 ) {
+ if ( ignoredLabels.indexOf(key) >= 0 ) {
// Skip ignored labels
return;
}
diff --git a/lib/shared/addon/utils/constants.js b/lib/shared/addon/utils/constants.js
index 8a4c3e0361..123a7106aa 100644
--- a/lib/shared/addon/utils/constants.js
+++ b/lib/shared/addon/utils/constants.js
@@ -165,6 +165,11 @@ var C = {
// node driver special fields
UI_HINTS: 'io.cattle.nodedriver/ui-field-hints',
+
+ K3S_NODE_ARGS: 'k3s.io/node-args',
+ K3S_NODE_CONFIG_HASH: 'k3s.io/node-config-hash',
+ K3S_NODE_ENV: 'k3s.io/node-env',
+ NODE_INSTANCE_TYPE: 'node.kubernetes.io/instance-type',
},
LANGUAGE: {
@@ -929,4 +934,6 @@ C.FEATURES = {
DASHBOARD: 'steve',
}
+C.NODES = { MASTER_NODE: 'node-role.kubernetes.io/master' };
+
export default C;
diff --git a/lib/shared/app/components/k3s-cluster-info/component.js b/lib/shared/app/components/k3s-cluster-info/component.js
new file mode 100644
index 0000000000..bb3d072d0a
--- /dev/null
+++ b/lib/shared/app/components/k3s-cluster-info/component.js
@@ -0,0 +1 @@
+export { default } from 'shared/components/k3s-cluster-info/component';
\ No newline at end of file
diff --git a/lib/shared/app/components/k3s-node-args/component.js b/lib/shared/app/components/k3s-node-args/component.js
new file mode 100644
index 0000000000..542bfa7a3f
--- /dev/null
+++ b/lib/shared/app/components/k3s-node-args/component.js
@@ -0,0 +1 @@
+export { default } from 'shared/components/k3s-node-args/component';
\ No newline at end of file
diff --git a/lib/shared/app/components/k3s-node-env-var/component.js b/lib/shared/app/components/k3s-node-env-var/component.js
new file mode 100644
index 0000000000..cc680339a5
--- /dev/null
+++ b/lib/shared/app/components/k3s-node-env-var/component.js
@@ -0,0 +1 @@
+export { default } from 'shared/components/k3s-node-env-var/component';
diff --git a/translations/en-us.yaml b/translations/en-us.yaml
index be6c390833..be3a50d18f 100644
--- a/translations/en-us.yaml
+++ b/translations/en-us.yaml
@@ -1472,6 +1472,13 @@ clusterDashboard:
memoryPressure: "Alert: Node {node} has memory pressure."
liveTitle: "{used} of {total} Used"
reserved: Reserved
+ k3sInfo:
+ title: K3S Information
+ detail: Node system information for K3S imported clusters.
+ nodeArgs:
+ title: K3S Node Arguments
+ detail: A read only list of the K3S arguments for this node.
+ noArgs: No node arguments
ingressResponse:
@@ -7094,6 +7101,26 @@ containersSection:
noMatch: No Containers match the current search
initContainer: Init Container
+k3sClusterInfo:
+ title: K3s Options
+ detail: Customize the K3S cluster options
+ workerConcurrency: Worker Concurrency
+ serverConcurrency: Server Concurrency
+ nodeInfo:
+ title: K3s Node Information
+ detail: Read-only view of K3S master node arguments and environment variables.
+ label: Master Node
+ datail: Additional K3S master node details
+ selectPrompt: Select node
+
+k3sNodeEnvVarSection:
+ title: K3S Node Environment Variables
+ detail: Read only list of environment variables for this K3S node.
+ key: Key
+ value: Value
+ noData: No environment variables
+ noMatch: No environment variables match the current search
+
labelsSection:
kind: Kind
title: Labels