diff --git a/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationData.js b/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationData.js
index c5f717839..912384ef1 100644
--- a/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationData.js
+++ b/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationData.js
@@ -4,5 +4,6 @@ angular.module('portainer.kubernetes').component('kubernetesConfigurationData',
bindings: {
formValues: '=',
isValid: '=',
+ isCreation: '=',
},
});
diff --git a/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationDataController.js b/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationDataController.js
index 1dcc43ce8..49d44307f 100644
--- a/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationDataController.js
+++ b/app/kubernetes/components/kubernetes-configuration-data/kubernetesConfigurationDataController.js
@@ -1,7 +1,10 @@
import angular from 'angular';
import _ from 'lodash-es';
-import { KubernetesConfigurationFormValuesDataEntry } from 'Kubernetes/models/configuration/formvalues';
+import chardet from 'chardet';
+import { Base64 } from 'js-base64';
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
+import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
+import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
class KubernetesConfigurationDataController {
/* @ngInject */
@@ -12,6 +15,8 @@ class KubernetesConfigurationDataController {
this.editorUpdateAsync = this.editorUpdateAsync.bind(this);
this.onFileLoad = this.onFileLoad.bind(this);
this.onFileLoadAsync = this.onFileLoadAsync.bind(this);
+ this.showSimpleMode = this.showSimpleMode.bind(this);
+ this.showAdvancedMode = this.showAdvancedMode.bind(this);
}
onChangeKey() {
@@ -20,7 +25,7 @@ class KubernetesConfigurationDataController {
}
addEntry() {
- this.formValues.Data.push(new KubernetesConfigurationFormValuesDataEntry());
+ this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
}
removeEntry(index) {
@@ -37,9 +42,20 @@ class KubernetesConfigurationDataController {
}
async onFileLoadAsync(event) {
- const entry = new KubernetesConfigurationFormValuesDataEntry();
+ const entry = new KubernetesConfigurationFormValuesEntry();
+ const encoding = chardet.detect(Buffer.from(event.target.result));
+ const decoder = new TextDecoder(encoding);
+
entry.Key = event.target.fileName;
- entry.Value = event.target.result;
+ entry.IsBinary = KubernetesConfigurationHelper.isBinary(encoding);
+
+ if (!entry.IsBinary) {
+ entry.Value = decoder.decode(event.target.result);
+ } else {
+ const stringValue = decoder.decode(event.target.result);
+ entry.Value = Base64.encode(stringValue);
+ }
+
this.formValues.Data.push(entry);
this.onChangeKey();
}
@@ -53,10 +69,20 @@ class KubernetesConfigurationDataController {
const temporaryFileReader = new FileReader();
temporaryFileReader.fileName = file.name;
temporaryFileReader.onload = this.onFileLoad;
- temporaryFileReader.readAsText(file);
+ temporaryFileReader.readAsArrayBuffer(file);
}
}
+ showSimpleMode() {
+ this.formValues.IsSimple = true;
+ this.formValues.Data = KubernetesConfigurationHelper.parseYaml(this.formValues);
+ }
+
+ showAdvancedMode() {
+ this.formValues.IsSimple = false;
+ this.formValues.DataYaml = KubernetesConfigurationHelper.parseData(this.formValues);
+ }
+
$onInit() {
this.state = {
duplicateKeys: {},
diff --git a/app/kubernetes/converters/configMap.js b/app/kubernetes/converters/configMap.js
index 1c3e09b63..820765519 100644
--- a/app/kubernetes/converters/configMap.js
+++ b/app/kubernetes/converters/configMap.js
@@ -1,8 +1,8 @@
import _ from 'lodash-es';
-import YAML from 'yaml';
import { KubernetesConfigMap } from 'Kubernetes/models/config-map/models';
import { KubernetesConfigMapCreatePayload, KubernetesConfigMapUpdatePayload } from 'Kubernetes/models/config-map/payloads';
import { KubernetesPortainerConfigurationOwnerLabel } from 'Kubernetes/models/configuration/models';
+import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
class KubernetesConfigMapConverter {
/**
@@ -16,7 +16,23 @@ class KubernetesConfigMapConverter {
res.ConfigurationOwner = data.metadata.labels ? data.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] : '';
res.CreationDate = data.metadata.creationTimestamp;
res.Yaml = yaml ? yaml.data : '';
- res.Data = data.data;
+
+ res.Data = _.concat(
+ _.map(data.data, (value, key) => {
+ const entry = new KubernetesConfigurationFormValuesEntry();
+ entry.Key = key;
+ entry.Value = value;
+ return entry;
+ }),
+ _.map(data.binaryData, (value, key) => {
+ const entry = new KubernetesConfigurationFormValuesEntry();
+ entry.Key = key;
+ entry.Value = value;
+ entry.IsBinary = true;
+ return entry;
+ })
+ );
+
return res;
}
@@ -41,7 +57,14 @@ class KubernetesConfigMapConverter {
res.metadata.namespace = data.Namespace;
const configurationOwner = _.truncate(data.ConfigurationOwner, { length: 63, omission: '' });
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = configurationOwner;
- res.data = data.Data;
+
+ _.forEach(data.Data, (entry) => {
+ if (entry.IsBinary) {
+ res.binaryData[entry.Key] = entry.Value;
+ } else {
+ res.data[entry.Key] = entry.Value;
+ }
+ });
return res;
}
@@ -54,7 +77,13 @@ class KubernetesConfigMapConverter {
res.metadata.name = data.Name;
res.metadata.namespace = data.Namespace;
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = data.ConfigurationOwner;
- res.data = data.Data;
+ _.forEach(data.Data, (entry) => {
+ if (entry.IsBinary) {
+ res.binaryData[entry.Key] = entry.Value;
+ } else {
+ res.data[entry.Key] = entry.Value;
+ }
+ });
return res;
}
@@ -64,18 +93,7 @@ class KubernetesConfigMapConverter {
res.Name = formValues.Name;
res.Namespace = formValues.ResourcePool.Namespace.Name;
res.ConfigurationOwner = formValues.ConfigurationOwner;
- if (formValues.IsSimple) {
- res.Data = _.reduce(
- formValues.Data,
- (acc, entry) => {
- acc[entry.Key] = entry.Value;
- return acc;
- },
- {}
- );
- } else {
- res.Data = YAML.parse(formValues.DataYaml);
- }
+ res.Data = formValues.Data;
return res;
}
}
diff --git a/app/kubernetes/converters/configuration.js b/app/kubernetes/converters/configuration.js
index 232fe5b79..e034fc2c6 100644
--- a/app/kubernetes/converters/configuration.js
+++ b/app/kubernetes/converters/configuration.js
@@ -1,3 +1,4 @@
+import _ from 'lodash-es';
import { KubernetesConfiguration, KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
class KubernetesConfigurationConverter {
@@ -9,7 +10,9 @@ class KubernetesConfigurationConverter {
res.Namespace = secret.Namespace;
res.CreationDate = secret.CreationDate;
res.Yaml = secret.Yaml;
- res.Data = secret.Data;
+ _.forEach(secret.Data, (entry) => {
+ res.Data[entry.Key] = entry.Value;
+ });
res.ConfigurationOwner = secret.ConfigurationOwner;
return res;
}
@@ -22,7 +25,9 @@ class KubernetesConfigurationConverter {
res.Namespace = configMap.Namespace;
res.CreationDate = configMap.CreationDate;
res.Yaml = configMap.Yaml;
- res.Data = configMap.Data;
+ _.forEach(configMap.Data, (entry) => {
+ res.Data[entry.Key] = entry.Value;
+ });
res.ConfigurationOwner = configMap.ConfigurationOwner;
return res;
}
diff --git a/app/kubernetes/converters/secret.js b/app/kubernetes/converters/secret.js
index 14eee19e5..72d37f070 100644
--- a/app/kubernetes/converters/secret.js
+++ b/app/kubernetes/converters/secret.js
@@ -1,17 +1,30 @@
import { KubernetesSecretCreatePayload, KubernetesSecretUpdatePayload } from 'Kubernetes/models/secret/payloads';
import { KubernetesApplicationSecret } from 'Kubernetes/models/secret/models';
-import YAML from 'yaml';
+import { KubernetesPortainerConfigurationDataAnnotation } from 'Kubernetes/models/configuration/models';
import _ from 'lodash-es';
import { KubernetesPortainerConfigurationOwnerLabel } from 'Kubernetes/models/configuration/models';
+import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
class KubernetesSecretConverter {
static createPayload(secret) {
const res = new KubernetesSecretCreatePayload();
res.metadata.name = secret.Name;
res.metadata.namespace = secret.Namespace;
- const configurationOwner = _.truncate(secret.configurationOwner, { length: 63, omission: '' });
+ const configurationOwner = _.truncate(secret.ConfigurationOwner, { length: 63, omission: '' });
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = configurationOwner;
- res.stringData = secret.Data;
+
+ let annotation = '';
+ _.forEach(secret.Data, (entry) => {
+ if (entry.IsBinary) {
+ res.data[entry.Key] = entry.Value;
+ annotation += annotation !== '' ? '|' + entry.Key : entry.Key;
+ } else {
+ res.stringData[entry.Key] = entry.Value;
+ }
+ });
+ if (annotation !== '') {
+ res.metadata.annotations[KubernetesPortainerConfigurationDataAnnotation] = annotation;
+ }
return res;
}
@@ -20,7 +33,19 @@ class KubernetesSecretConverter {
res.metadata.name = secret.Name;
res.metadata.namespace = secret.Namespace;
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = secret.ConfigurationOwner;
- res.stringData = secret.Data;
+
+ let annotation = '';
+ _.forEach(secret.Data, (entry) => {
+ if (entry.IsBinary) {
+ res.data[entry.Key] = entry.Value;
+ annotation += annotation !== '' ? '|' + entry.Key : entry.Key;
+ } else {
+ res.stringData[entry.Key] = entry.Value;
+ }
+ });
+ if (annotation !== '') {
+ res.metadata.annotations[KubernetesPortainerConfigurationDataAnnotation] = annotation;
+ }
return res;
}
@@ -32,7 +57,21 @@ class KubernetesSecretConverter {
res.ConfigurationOwner = payload.metadata.labels ? payload.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] : '';
res.CreationDate = payload.metadata.creationTimestamp;
res.Yaml = yaml ? yaml.data : '';
- res.Data = payload.data;
+
+ res.Data = _.map(payload.data, (value, key) => {
+ const annotations = payload.metadata.annotations ? payload.metadata.annotations[KubernetesPortainerConfigurationDataAnnotation] : '';
+ const entry = new KubernetesConfigurationFormValuesEntry();
+ entry.Key = key;
+ entry.IsBinary = _.includes(annotations, entry.Key);
+
+ if (!entry.IsBinary) {
+ entry.Value = atob(value);
+ } else {
+ entry.Value = value;
+ }
+ return entry;
+ });
+
return res;
}
@@ -41,18 +80,7 @@ class KubernetesSecretConverter {
res.Name = formValues.Name;
res.Namespace = formValues.ResourcePool.Namespace.Name;
res.ConfigurationOwner = formValues.ConfigurationOwner;
- if (formValues.IsSimple) {
- res.Data = _.reduce(
- formValues.Data,
- (acc, entry) => {
- acc[entry.Key] = entry.Value;
- return acc;
- },
- {}
- );
- } else {
- res.Data = YAML.parse(formValues.DataYaml);
- }
+ res.Data = formValues.Data;
return res;
}
}
diff --git a/app/kubernetes/helpers/configurationHelper.js b/app/kubernetes/helpers/configurationHelper.js
index ee8acb9e4..e15929195 100644
--- a/app/kubernetes/helpers/configurationHelper.js
+++ b/app/kubernetes/helpers/configurationHelper.js
@@ -1,5 +1,7 @@
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
+import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
import _ from 'lodash-es';
+import YAML from 'yaml';
class KubernetesConfigurationHelper {
static getUsingApplications(config, applications) {
@@ -21,6 +23,10 @@ class KubernetesConfigurationHelper {
return _.startsWith(config.Name, 'default-token-');
}
+ static isBinary(encoding) {
+ return encoding !== '' && !_.includes(encoding, 'ISO') && !_.includes(encoding, 'UTF');
+ }
+
static setConfigurationUsed(config) {
config.Used = config.Applications && config.Applications.length !== 0;
}
@@ -32,6 +38,31 @@ class KubernetesConfigurationHelper {
});
}
+ static parseYaml(formValues) {
+ YAML.defaultOptions.customTags = ['binary'];
+ const data = _.map(YAML.parse(formValues.DataYaml), (value, key) => {
+ const entry = new KubernetesConfigurationFormValuesEntry();
+ entry.Key = key;
+ entry.Value = value;
+ const oldEntry = _.find(formValues.Data, { Key: entry.Key });
+ entry.IsBinary = oldEntry ? oldEntry.IsBinary : false;
+ return entry;
+ });
+ return data;
+ }
+
+ static parseData(formValues) {
+ const data = _.reduce(
+ formValues.Data,
+ (acc, entry) => {
+ acc[entry.Key] = entry.Value;
+ return acc;
+ },
+ {}
+ );
+ return YAML.stringify(data);
+ }
+
static isExternalConfiguration(configuration) {
return !configuration.ConfigurationOwner;
}
diff --git a/app/kubernetes/models/config-map/models.js b/app/kubernetes/models/config-map/models.js
index b0463042c..151a03764 100644
--- a/app/kubernetes/models/config-map/models.js
+++ b/app/kubernetes/models/config-map/models.js
@@ -11,7 +11,7 @@ const _KubernetesConfigMap = Object.freeze({
Namespace: '',
Yaml: '',
ConfigurationOwner: '',
- Data: {},
+ Data: [],
});
export class KubernetesConfigMap {
diff --git a/app/kubernetes/models/config-map/payloads.js b/app/kubernetes/models/config-map/payloads.js
index 14e8fe708..3f2bf873d 100644
--- a/app/kubernetes/models/config-map/payloads.js
+++ b/app/kubernetes/models/config-map/payloads.js
@@ -6,6 +6,7 @@ import { KubernetesCommonMetadataPayload } from 'Kubernetes/models/common/payloa
const _KubernetesConfigMapCreatePayload = Object.freeze({
metadata: new KubernetesCommonMetadataPayload(),
data: {},
+ binaryData: {},
});
export class KubernetesConfigMapCreatePayload {
constructor() {
@@ -19,6 +20,7 @@ export class KubernetesConfigMapCreatePayload {
const _KubernetesConfigMapUpdatePayload = Object.freeze({
metadata: new KubernetesCommonMetadataPayload(),
data: {},
+ binaryData: {},
});
export class KubernetesConfigMapUpdatePayload {
constructor() {
diff --git a/app/kubernetes/models/configuration/formvalues.js b/app/kubernetes/models/configuration/formvalues.js
index 859542651..4c6d161cb 100644
--- a/app/kubernetes/models/configuration/formvalues.js
+++ b/app/kubernetes/models/configuration/formvalues.js
@@ -20,16 +20,14 @@ export class KubernetesConfigurationFormValues {
}
}
-/**
- * KubernetesConfigurationEntry Model
- */
-const _KubernetesConfigurationFormValuesDataEntry = Object.freeze({
+const _KubernetesConfigurationFormValuesEntry = Object.freeze({
Key: '',
Value: '',
+ IsBinary: false,
});
-export class KubernetesConfigurationFormValuesDataEntry {
+export class KubernetesConfigurationFormValuesEntry {
constructor() {
- Object.assign(this, JSON.parse(JSON.stringify(_KubernetesConfigurationFormValuesDataEntry)));
+ Object.assign(this, JSON.parse(JSON.stringify(_KubernetesConfigurationFormValuesEntry)));
}
}
diff --git a/app/kubernetes/models/configuration/models.js b/app/kubernetes/models/configuration/models.js
index 08c5ae90a..e34b07f25 100644
--- a/app/kubernetes/models/configuration/models.js
+++ b/app/kubernetes/models/configuration/models.js
@@ -1,4 +1,5 @@
export const KubernetesPortainerConfigurationOwnerLabel = 'io.portainer.kubernetes.configuration.owner';
+export const KubernetesPortainerConfigurationDataAnnotation = 'io.portainer.kubernetes.configuration.data';
/**
* Configuration Model (Composite)
diff --git a/app/kubernetes/models/secret/models.js b/app/kubernetes/models/secret/models.js
index ae47b8b2e..d9d4316e7 100644
--- a/app/kubernetes/models/secret/models.js
+++ b/app/kubernetes/models/secret/models.js
@@ -8,7 +8,7 @@ const _KubernetesApplicationSecret = Object.freeze({
CreationDate: '',
ConfigurationOwner: '',
Yaml: '',
- Data: {},
+ Data: [],
});
export class KubernetesApplicationSecret {
diff --git a/app/kubernetes/models/secret/payloads.js b/app/kubernetes/models/secret/payloads.js
index 2ce761d04..82ae4a3ea 100644
--- a/app/kubernetes/models/secret/payloads.js
+++ b/app/kubernetes/models/secret/payloads.js
@@ -7,6 +7,7 @@ const _KubernetesSecretCreatePayload = Object.freeze({
metadata: new KubernetesCommonMetadataPayload(),
type: 'Opaque',
data: {},
+ stringData: {},
});
export class KubernetesSecretCreatePayload {
@@ -22,6 +23,7 @@ const _KubernetesSecretUpdatePayload = Object.freeze({
metadata: new KubernetesCommonMetadataPayload(),
type: 'Opaque',
data: {},
+ stringData: {},
});
export class KubernetesSecretUpdatePayload {
diff --git a/app/kubernetes/views/configurations/create/createConfiguration.html b/app/kubernetes/views/configurations/create/createConfiguration.html
index b258473f2..c490f4fad 100644
--- a/app/kubernetes/views/configurations/create/createConfiguration.html
+++ b/app/kubernetes/views/configurations/create/createConfiguration.html
@@ -115,8 +115,12 @@
official documentation.