Skip to content

Commit 79789ee

Browse files
committed
Merge branch 'secrets-group-by-table' into 'main'
WIP: Allow any k8s secret keys, use group-by table for edit See merge request weblogic-cloud/weblogic-toolkit-ui!268
2 parents 4c8e334 + d63646f commit 79789ee

23 files changed

+1286
-593
lines changed

electron/app/js/imageBuilderUtils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ function getDockerEnv(httpsProxyUrl, bypassProxyHosts, imageBuilderOptions) {
152152
}
153153

154154
let env = {
155-
DOCKER_BUILDKIT: '0',
155+
// DOCKER_BUILDKIT: '0',
156156
// podman relies on the PATH including other executables (e.g., newuidmap)...
157157
PATH: parentPath
158158
};

electron/app/locales/en/webui.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"dialog-button-cancel": "Cancel",
2727
"dialog-button-yes": "Yes",
2828
"dialog-button-no": "No",
29+
"dialog-button-delete": "Delete",
2930
"docker-hub": "Docker Hub",
3031

3132
"script-selector-label": "Shell Script Type",
@@ -833,13 +834,25 @@
833834
"domain-design-propoverride-header": "Model Variable Override Value",
834835
"domain-design-secrets-table-aria-label": "Secrets Table",
835836
"domain-design-secretname-header": "Secret Name",
836-
"domain-design-username-header": "Username",
837-
"domain-design-password-header": "Password",
838-
"domain-design-secrets-cell-field-name": "Secrets table {{name}} field at row {{position}} with UID {{uid}}",
837+
"domain-design-secret-key-header": "Field Name",
838+
"domain-design-secret-value-header": "Field Value",
839+
"domain-design-secret-name-field-name": "Secret name",
840+
"domain-design-secret-key-name": "Secret {{secretName}} field name",
841+
"domain-design-secret-value-name": "Secret {{secretName}} field {{fieldName}} field value",
839842
"domain-design-add-external-secret-tooltip": "Add External Model Secret Entry",
840843
"domain-design-delete-external-secret-tooltip": "Delete External Model Secret Entry",
841844
"domain-design-no-secrets-message": "No additional model secrets found.",
842845
"domain-design-no-secrets-add-remove-message": "No additional external model secrets found. Please add secrets for any secret references in the external model that are not already handled in other parts of this page.",
846+
"domain-design-edit-secret-tooltip": "Edit External Model Secret",
847+
848+
"domain-design-secret-dialog-add-title": "Add Secret",
849+
"domain-design-secret-dialog-edit-title": "Edit Secret {{name}}",
850+
"domain-design-secret-name-help": "Enter the name for the secret",
851+
"domain-design-secret-name-label": "Secret Name",
852+
"domain-design-secret-no-key-message": "No secret keys are specified",
853+
"domain-design-secret-add-key-tooltip": "Add Secret Key",
854+
"domain-design-secret-delete-key-tooltip": "Delete Secret Key",
855+
"domain-design-secret-no-keys-message": "Add one or more secret keys.",
843856

844857
"domain-design-domain-env-var-override-label": "Override Existing Environment Variable Values",
845858
"domain-design-domain-env-var-override-help": "Other fields in the Domain-Wide Server Settings use the JAVA_OPTIONS and USER_MEM_ARGS environment variables. By default, any value for those variables in the table below are appended to the existing values. Enabling this override switch will change that behavior to cause the value specified in the table to overwrite any existing values.",
@@ -1763,6 +1776,8 @@
17631776
"vz-component-deployer-create-image-pull-secret-error-message": "Unable to deploy Verrazzano component(s) due to an error creating the image pull secret {{secretName}} in the Kubernetes namespace {{namespace}}: {{error}}",
17641777
"vz-component-deployer-create-runtime-secret-in-progress": "Creating runtime encryption secret {{secretName}} in Kubernetes namespace {{namespace}}",
17651778
"vz-component-deployer-create-runtime-secret-error-message": "Unable to deploy Verrazzano component(s) due to an error creating the runtime encryption secret {{secretName}} in the Kubernetes namespace {{namespace}}: {{error}}",
1779+
"vz-component-deployer-create-wallet-password-secret-in-progress": "Creating OPSS wallet password secret {{secretName}} in Kubernetes namespace {{namespace}}",
1780+
"vz-component-deployer-create-wallet-password-secret-error-message": "Unable to deploy Verrazzano component(s) due to an error creating the OPSS wallet password secret {{secretName}} in the Kubernetes namespace {{namespace}}: {{error}}",
17661781
"vz-component-deployer-create-wl-secret-in-progress": "Creating secret {{secretName}} for WebLogic domain {{domainName}} in Kubernetes namespace {{namespace}}",
17671782
"vz-component-deployer-create-wl-secret-failed-error-message": "Failed to create WebLogic credentials secret {{secretName}} in Kubernetes namespace {{namespace}}: {{error}}",
17681783
"vz-component-deployer-create-secrets-in-progress": "Creating secrets for WebLogic domain {{domainName}} in Kubernetes namespace {{namespace}}",

webui/src/css/app.css

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@
8585
overflow: auto;
8686
}
8787

88+
/* group-by table grouping rows */
89+
.wkt-table-group-row {
90+
background-color: #d7d7d7;
91+
}
92+
93+
/* for non-editable text in table edit mode */
94+
.wkt-table-no-edit-text {
95+
padding: 0 0.5rem;
96+
}
97+
8898
/* collapsible console panel, on every page */
8999

90100
.wkt-console-section .oj-collapsible-header-wrapper {
@@ -418,6 +428,11 @@ h6:first-child {
418428
overflow: auto;
419429
}
420430

431+
.wkt-secret-edit-dialog {
432+
width: 700px;
433+
height: auto;
434+
}
435+
421436
.wkt-validation-error-dialog {
422437
width: 75%;
423438
height: auto;
@@ -767,6 +782,25 @@ h6.wkt-panel-heading {
767782
font-weight: bold;
768783
}
769784

785+
.wkt-user-assistance {
786+
color: var(--oj-user-assistance-inline-text-color);
787+
font-size: var(--oj-user-assistance-inline-font-size);
788+
margin-top: 2px;
789+
}
790+
791+
.wkt-user-assistance-error-icon {
792+
font-family: "JetInternal IconFont", serif;
793+
font-size: 1rem;
794+
vertical-align: middle;
795+
line-height:16px;
796+
}
797+
798+
.wkt-user-assistance-error-icon:before {
799+
color: var(--oj-core-danger-3);
800+
content: "\f145";
801+
padding-right: 4px;
802+
}
803+
770804
.wkt-placeholder-text {
771805
color: var(--oj-label-inside-edge-color);
772806
}

webui/src/js/models/k8s-domain-definition.js

Lines changed: 111 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,32 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
9595
this.runtimeSecretValue = props.createProperty(window.api.utils.generateUuid()).asCredential();
9696
this.introspectorJobActiveDeadlineSeconds = props.createProperty(900);
9797

98-
this.secrets = props.createListProperty(['uid', 'name', 'username', 'password']).persistByKey('uid');
98+
const internalFields = {
99+
// this internal observable list property is used for JSON read/write of this.secrets
100+
secrets: props.createListProperty(['uid', 'name', 'keys']).persistByKey('uid')
101+
};
102+
103+
// this.secrets supports .observable() and .value, like other observable properties.
104+
// it is synchronized with internalFields.secrets during JSON read/write.
105+
// each secret in this.secrets has flattened keys structure, like:
106+
//
107+
// uid: "YdPQBG9Y/",
108+
// name: "my-secret",
109+
// keys: [{
110+
// uid: "P51SNv9WA",
111+
// key: "username",
112+
// value: "me"
113+
// }, {
114+
// uid: "P51SNv9WA",
115+
// key: "password",
116+
// value: "welcome1"
117+
// }]
118+
this.secrets = {
119+
observable: ko.observableArray(),
120+
get value() {
121+
return this.observable();
122+
}
123+
};
99124

100125
this.replicas = props.createProperty(2);
101126
// TODO - can a WebLogic server really run with 64MB? If not, raise minimum limit...
@@ -165,17 +190,56 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
165190

166191
// update the secrets list when the uid changes.
167192
this.uid.observable.subscribe(() => {
168-
this.updateSecrets();
193+
this.updateSecretsFromModel();
169194
});
170195

171196
// update the secrets list when any model content changes.
172197
wdtModel.modelContentChanged.subscribe(() => {
173198
wktLogger.debug('modelContentChanged event calling updateSecrets()');
174-
this.updateSecrets();
199+
this.updateSecretsFromModel();
200+
});
201+
202+
this.updateFromInternalFields = (() => {
203+
const flattenedSecrets = [];
204+
internalFields.secrets.observable().forEach(secret => {
205+
// flatten the key map to a list
206+
const newKeys = [];
207+
for(const keyUid in secret.keys) {
208+
const keyData = secret.keys[keyUid];
209+
newKeys.push({
210+
uid: keyUid,
211+
key: keyData.key,
212+
value: keyData.value
213+
});
214+
}
215+
const newSecret = {...secret, keys: newKeys};
216+
flattenedSecrets.push(newSecret);
217+
});
218+
this.secrets.observable(flattenedSecrets);
219+
});
220+
221+
this.updateInternalFields = (() => {
222+
const mappedSecrets = [];
223+
this.secrets.observable().forEach(secret => {
224+
// create a map from each flattened key
225+
const mappedKeys = {};
226+
secret.keys.forEach(flatSecret => {
227+
mappedKeys[flatSecret.uid] = {
228+
key: flatSecret.key,
229+
value: flatSecret.value
230+
};
231+
});
232+
const newSecret = {...secret, keys: mappedKeys};
233+
mappedSecrets.push(newSecret);
234+
});
235+
internalFields.secrets.observable(mappedSecrets);
175236
});
176237

177238
this.readFrom = (json) => {
178239
props.createGroup(name, this).readFrom(json);
240+
241+
props.createGroup(name, internalFields).readFrom(json);
242+
this.updateFromInternalFields();
179243
};
180244

181245
this.loadPropertyOverrideValues = (json) => {
@@ -189,22 +253,22 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
189253
};
190254

191255
this.setCredentialPathsForSecretsTable = (json) => {
192-
wktLogger.debug('entering setCredentialPathsForSecretsTable() with secrets table length = %s', this.secrets.value.length);
193-
if (this.secrets.value.length > 0) {
256+
const secrets = this.secrets.observable();
257+
wktLogger.debug('entering setCredentialPathsForSecretsTable() with secrets table length = %s', secrets.length);
258+
if (secrets.length > 0) {
194259
if (!json.credentialPaths) {
195260
wktLogger.debug('creating credentialPaths array');
196261
json.credentialPaths = [];
197262
}
198-
for (const secret of this.secrets.value) {
263+
for (const secret of secrets) {
199264
wktLogger.debug('working on secret path %s', `${name}.secrets.${secret.uid}`);
200-
if (secret.username) {
201-
wktLogger.debug('setting secret %s', `${name}.secrets.${secret.uid}.username`);
202-
json.credentialPaths.push(`${name}.secrets.${secret.uid}.username`);
203-
}
204-
if (secret.password) {
205-
wktLogger.debug('setting secret %s', `${name}.secrets.${secret.uid}.password`);
206-
json.credentialPaths.push(`${name}.secrets.${secret.uid}.password`);
207-
}
265+
secret.keys.forEach(keyData => {
266+
if(keyData.value) {
267+
const keyUid = keyData.uid;
268+
wktLogger.debug('setting secret %s', `${name}.secrets.${secret.uid}.keys.${keyUid}.value`);
269+
json.credentialPaths.push(`${name}.secrets.${secret.uid}.keys.${keyUid}.value`);
270+
}
271+
});
208272
}
209273
}
210274
};
@@ -213,6 +277,9 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
213277
this.setCredentialPathsForSecretsTable(json);
214278
props.createGroup(name, this).writeTo(json);
215279

280+
this.updateInternalFields();
281+
props.createGroup(name, internalFields).writeTo(json);
282+
216283
// Force the generated runtime secret to be written to the project.
217284
// This will allow us to keep the same generated password for the life
218285
// of the project (assuming the user doesn't intentionally change it).
@@ -241,10 +308,18 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
241308
};
242309

243310
this.isChanged = () => {
311+
this.updateInternalFields();
312+
if(props.createGroup(name, internalFields).isChanged()) {
313+
return true;
314+
}
315+
244316
return props.createGroup(name, this).isChanged();
245317
};
246318

247319
this.setNotChanged = () => {
320+
this.updateInternalFields();
321+
props.createGroup(name, internalFields).setNotChanged();
322+
248323
props.createGroup(name, this).setNotChanged();
249324
};
250325

@@ -352,20 +427,28 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
352427
};
353428
};
354429

430+
// search existing secrets for an existing secret/key value
355431
this.getFieldValueFromExistingSecrets = (uid, fieldName, defaultValue) => {
356432
let result = defaultValue;
357433
for (const domainSecret of this.secrets.observable()) {
358434
if (domainSecret.uid === uid) {
359-
if (domainSecret[fieldName]) {
360-
result = domainSecret[fieldName];
435+
for(const keyMap of domainSecret.keys) {
436+
if (keyMap.key === fieldName) {
437+
result = keyMap.value;
438+
break;
439+
}
361440
}
362-
break;
363441
}
364442
}
365443
return result;
366444
};
367445

368-
this.updateSecrets = () => {
446+
this.updateSecretsFromModel = () => {
447+
const auxImageHelper = require('utils/aux-image-helper');
448+
if(auxImageHelper.projectUsingExternalImageContainingModel()) {
449+
return;
450+
}
451+
369452
const modelSecretsData = wdtModel.getModelSecretsData();
370453

371454
const modelSecrets = new Map();
@@ -374,14 +457,21 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
374457
const key = modelSecretData.envVar ? `${modelSecretData.envVar}${modelSecretData.name}` : modelSecretData.name;
375458
const uid = utils.hashIt(key);
376459

460+
const secretKeys = [];
461+
for(const secretKey in modelSecretData.keys) {
462+
const keyUid = utils.getShortUuid();
463+
const existing_value = this.getFieldValueFromExistingSecrets(uid, secretKey,
464+
modelSecretData.keys[secretKey]);
465+
secretKeys.push({uid: keyUid, key: secretKey, value: existing_value});
466+
}
467+
377468
modelSecrets.set(uid, {
378469
uid: uid,
379470
name: computeSecretNameFromModelData(modelSecretData, envVarMap),
380-
username: this.getFieldValueFromExistingSecrets(uid, 'username', modelSecretData.username),
381-
password: this.getFieldValueFromExistingSecrets(uid, 'password', modelSecretData.password)
471+
keys: secretKeys
382472
});
383473
}
384-
this.secrets.value = [...modelSecrets.values()];
474+
this.secrets.observable([...modelSecrets.values()]);
385475
};
386476

387477
function computeSecretNameFromModelData(modelSecretData, envVarMap) {

webui/src/js/models/wdt-model-definition.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,12 @@ define(['knockout', 'utils/observable-properties', 'js-yaml', 'utils/validation-
172172
if (secretsMap.has(secretKey)) {
173173
secretData = secretsMap.get(secretKey);
174174
} else {
175-
secretData = { name: secretName };
175+
secretData = { name: secretName, keys: {} };
176176
if (secretEnvVar) {
177177
secretData.envVar = secretEnvVar;
178178
}
179179
}
180-
secretData[secretField] = '';
180+
secretData.keys[secretField] = '';
181181
secretsMap.set(secretKey, secretData);
182182
} else {
183183
wktLogger.debug('skipping matching secret %s', matches.groups.name);

webui/src/js/models/wkt-project.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,28 @@ function (ko, wdtConstructor, imageConstructor, kubectlConstructor, domainConstr
151151
wktProjectJson.k8sDomain.clusters = newClusters;
152152
}
153153
}
154+
155+
// Version 1.6 changes domain secrets to allow any keys.
156+
// Prior versions allowed only username and password.
157+
// If username or password attributes are present, convert them to objects in the keys map.
158+
//
159+
if ('k8sDomain' in wktProjectJson && 'secrets' in wktProjectJson.k8sDomain) {
160+
const secrets = wktProjectJson.k8sDomain.secrets;
161+
for(const secretUid in secrets) {
162+
const secret = secrets[secretUid];
163+
if (!secret.hasOwnProperty('keys')) {
164+
const keyMap = {};
165+
['username', 'password'].forEach(attribute_key => {
166+
if(secret.hasOwnProperty(attribute_key)) {
167+
const keyUid = utils.getShortUuid();
168+
keyMap[keyUid] = {key: attribute_key, value: secret[attribute_key]};
169+
delete secret[attribute_key];
170+
}
171+
});
172+
secret['keys'] = keyMap;
173+
}
174+
}
175+
}
154176
};
155177

156178
this.setFromJson = (wktProjectJson, modelContentsJson) => {

webui/src/js/utils/k8s-domain-actions-base.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license
3-
* Copyright (c) 2021, Oracle and/or its affiliates.
3+
* Copyright (c) 2021, 2023, Oracle and/or its affiliates.
44
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
55
*/
66
'use strict';
@@ -172,6 +172,15 @@ function (WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper) {
172172
}
173173
return false;
174174
}
175+
176+
getSecretValue(secret, key) {
177+
for(const secretKey of secret.keys) {
178+
if(secretKey.key === key) {
179+
return secretKey.value;
180+
}
181+
}
182+
return null;
183+
}
175184
}
176185

177186
return K8sDomainActionsBase;

0 commit comments

Comments
 (0)