Skip to content

Commit 6d58808

Browse files
authored
Owls 87956 - Generate shorter volume name when override secret name is too long (#2257)
* OWLS-87956 - Generate shorter volume name when override secret name is too long
1 parent a2533f2 commit 6d58808

File tree

4 files changed

+171
-6
lines changed

4 files changed

+171
-6
lines changed

operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import oracle.kubernetes.operator.calls.UnrecoverableErrorBuilder;
3232
import oracle.kubernetes.operator.logging.LoggingFacade;
3333
import oracle.kubernetes.operator.logging.LoggingFactory;
34+
import oracle.kubernetes.operator.utils.ChecksumUtils;
3435
import oracle.kubernetes.operator.work.NextAction;
3536
import oracle.kubernetes.operator.work.Packet;
3637
import oracle.kubernetes.operator.work.Step;
@@ -44,6 +45,10 @@ public abstract class JobStepContext extends BasePodStepContext {
4445
private static final LoggingFacade LOGGER = LoggingFactory.getLogger("Operator", "Operator");
4546
private static final String WEBLOGIC_OPERATOR_SCRIPTS_INTROSPECT_DOMAIN_SH =
4647
"/weblogic-operator/scripts/introspectDomain.sh";
48+
private static final int MAX_ALLOWED_VOLUME_NAME_LENGTH = 63;
49+
public static final String VOLUME_NAME_SUFFIX = "-volume";
50+
public static final String CONFIGMAP_TYPE = "cm";
51+
public static final String SECRET_TYPE = "st";
4752
private V1Job jobModel;
4853

4954
JobStepContext(Packet packet) {
@@ -313,14 +318,14 @@ protected V1PodSpec createPodSpec(TuningParameters tuningParameters) {
313318
private void addConfigOverrideSecretVolume(V1PodSpec podSpec, String secretName) {
314319
podSpec.addVolumesItem(
315320
new V1Volume()
316-
.name(secretName + "-volume")
321+
.name(getVolumeName(secretName, SECRET_TYPE))
317322
.secret(getOverrideSecretVolumeSource(secretName)));
318323
}
319324

320325
private void addConfigOverrideVolume(V1PodSpec podSpec, String configOverrides) {
321326
podSpec.addVolumesItem(
322327
new V1Volume()
323-
.name(configOverrides + "-volume")
328+
.name(getVolumeName(configOverrides, CONFIGMAP_TYPE))
324329
.configMap(getOverridesVolumeSource(configOverrides)));
325330
}
326331

@@ -331,7 +336,7 @@ private boolean isSourceWdt() {
331336
private void addWdtConfigMapVolume(V1PodSpec podSpec, String configMapName) {
332337
podSpec.addVolumesItem(
333338
new V1Volume()
334-
.name(configMapName + "-volume")
339+
.name(getVolumeName(configMapName, CONFIGMAP_TYPE))
335340
.configMap(getWdtConfigMapVolumeSource(configMapName)));
336341
}
337342

@@ -365,20 +370,20 @@ protected V1Container createPrimaryContainer(TuningParameters tuningParameters)
365370

366371
if (getConfigOverrides() != null && getConfigOverrides().length() > 0) {
367372
container.addVolumeMountsItem(
368-
readOnlyVolumeMount(getConfigOverrides() + "-volume", OVERRIDES_CM_MOUNT_PATH));
373+
readOnlyVolumeMount(getVolumeName(getConfigOverrides(), CONFIGMAP_TYPE), OVERRIDES_CM_MOUNT_PATH));
369374
}
370375

371376
List<String> configOverrideSecrets = getConfigOverrideSecrets();
372377
for (String secretName : configOverrideSecrets) {
373378
container.addVolumeMountsItem(
374379
readOnlyVolumeMount(
375-
secretName + "-volume", OVERRIDE_SECRETS_MOUNT_PATH + '/' + secretName));
380+
getVolumeName(secretName, SECRET_TYPE), OVERRIDE_SECRETS_MOUNT_PATH + '/' + secretName));
376381
}
377382

378383
if (isSourceWdt()) {
379384
if (getWdtConfigMap() != null) {
380385
container.addVolumeMountsItem(
381-
readOnlyVolumeMount(getWdtConfigMap() + "-volume", WDTCONFIGMAP_MOUNT_PATH));
386+
readOnlyVolumeMount(getVolumeName(getWdtConfigMap(), CONFIGMAP_TYPE), WDTCONFIGMAP_MOUNT_PATH));
382387
}
383388
container.addVolumeMountsItem(
384389
readOnlyVolumeMount(RUNTIME_ENCRYPTION_SECRET_VOLUME,
@@ -389,6 +394,22 @@ protected V1Container createPrimaryContainer(TuningParameters tuningParameters)
389394
return container;
390395
}
391396

397+
private String getVolumeName(String resourceName, String type) {
398+
return getName(resourceName, type);
399+
}
400+
401+
private String getName(String resourceName, String type) {
402+
return resourceName.length() > (MAX_ALLOWED_VOLUME_NAME_LENGTH - VOLUME_NAME_SUFFIX.length())
403+
? getShortName(resourceName, type)
404+
: resourceName + VOLUME_NAME_SUFFIX;
405+
}
406+
407+
private String getShortName(String resourceName, String type) {
408+
String volumeSuffix = VOLUME_NAME_SUFFIX + "-" + type + "-"
409+
+ Optional.ofNullable(ChecksumUtils.getMD5Hash(resourceName)).orElse("");
410+
return resourceName.substring(0, MAX_ALLOWED_VOLUME_NAME_LENGTH - volumeSuffix.length()) + volumeSuffix;
411+
}
412+
392413
protected String getContainerName() {
393414
return getJobName();
394415
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2021, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
package oracle.kubernetes.operator.utils;
5+
6+
import java.security.MessageDigest;
7+
import javax.xml.bind.DatatypeConverter;
8+
9+
import oracle.kubernetes.operator.logging.LoggingFacade;
10+
import oracle.kubernetes.operator.logging.LoggingFactory;
11+
import oracle.kubernetes.operator.logging.MessageKeys;
12+
13+
public class ChecksumUtils {
14+
private static final LoggingFacade LOGGER = LoggingFactory.getLogger("Operator", "Operator");
15+
16+
/**
17+
* Gets the MD5 hash of a string.
18+
*
19+
* @param data input string
20+
* @return MD5 hash value of the data, null in case of an exception.
21+
*/
22+
public static String getMD5Hash(String data) {
23+
try {
24+
return bytesToHex(MessageDigest.getInstance("MD5").digest(data.getBytes("UTF-8")));
25+
} catch (Exception ex) {
26+
LOGGER.severe(MessageKeys.EXCEPTION, ex);
27+
return null;
28+
}
29+
}
30+
31+
private static String bytesToHex(byte[] hash) {
32+
return DatatypeConverter.printHexBinary(hash).toLowerCase();
33+
}
34+
}

operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
import io.kubernetes.client.openapi.models.V1PodTemplateSpec;
2828
import io.kubernetes.client.openapi.models.V1SecurityContext;
2929
import io.kubernetes.client.openapi.models.V1Toleration;
30+
import io.kubernetes.client.openapi.models.V1Volume;
3031
import io.kubernetes.client.openapi.models.V1VolumeMount;
32+
import oracle.kubernetes.operator.DomainSourceType;
3133
import oracle.kubernetes.operator.JobAwaiterStepFactory;
3234
import oracle.kubernetes.operator.LabelConstants;
3335
import oracle.kubernetes.operator.ProcessingConstants;
@@ -57,9 +59,11 @@
5759
import static oracle.kubernetes.operator.DomainProcessorTestSetup.createTestDomain;
5860
import static oracle.kubernetes.operator.ProcessingConstants.DOMAIN_TOPOLOGY;
5961
import static oracle.kubernetes.operator.ProcessingConstants.JOBWATCHER_COMPONENT_NAME;
62+
import static oracle.kubernetes.operator.helpers.Matchers.hasConfigMapVolume;
6063
import static oracle.kubernetes.operator.helpers.Matchers.hasContainer;
6164
import static oracle.kubernetes.operator.helpers.Matchers.hasEnvVar;
6265
import static oracle.kubernetes.operator.helpers.Matchers.hasEnvVarRegEx;
66+
import static oracle.kubernetes.operator.helpers.Matchers.hasSecretVolume;
6367
import static oracle.kubernetes.operator.helpers.Matchers.hasVolumeMount;
6468
import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createAffinity;
6569
import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createConfigMapKeyRefEnvVar;
@@ -69,6 +73,7 @@
6973
import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createSecretKeyRefEnvVar;
7074
import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createSecurityContext;
7175
import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createToleration;
76+
import static oracle.kubernetes.operator.utils.ChecksumUtils.getMD5Hash;
7277
import static org.hamcrest.CoreMatchers.equalTo;
7378
import static org.hamcrest.CoreMatchers.is;
7479
import static org.hamcrest.Matchers.allOf;
@@ -85,13 +90,29 @@
8590
public class JobHelperTest extends DomainValidationBaseTest {
8691
private static final String RAW_VALUE_1 = "find uid1 at $(DOMAIN_HOME)";
8792
private static final String END_VALUE_1 = "find uid1 at /u01/oracle/user_projects/domains";
93+
protected static final String LONG_RESOURCE_NAME
94+
= "very-long-resource-name-very-long-resource-name-abcdefghi";
95+
protected static final String SECOND_LONG_RESOURCE_NAME
96+
= "very-long-resource-name-very-long-resource-name-abcdefghijklmnopqrstuvwxyz";
8897

8998
/**
9099
* OEVN is the name of an env var that contains a comma-separated list of oper supplied env var names.
91100
* It's used by the Model in Image introspector job to detect env var differences from the last
92101
* time the job ran.
93102
*/
94103
private static final String OEVN = "OPERATOR_ENVVAR_NAMES";
104+
public static final String SECRET_VOLUME_SUFFIX1 = "-volume-st-" + getMD5Hash(LONG_RESOURCE_NAME);
105+
public static final String SECRET_VOLUME_SUFFIX2 = "-volume-st-" + getMD5Hash(SECOND_LONG_RESOURCE_NAME);
106+
public static final String CM_VOLUME_SUFFIX1 = "-volume-cm-" + getMD5Hash(LONG_RESOURCE_NAME);
107+
public static final int MAX_ALLOWED_VOLUME_NAME_LENGTH = 63;
108+
public static final String VOLUME_NAME_FOR_LONG_SECRET_NAME = LONG_RESOURCE_NAME
109+
.substring(0, MAX_ALLOWED_VOLUME_NAME_LENGTH - SECRET_VOLUME_SUFFIX1.length()) + SECRET_VOLUME_SUFFIX1;
110+
public static final String VOLUME_NAME_FOR_SECOND_LONG_SECRET_NAME = SECOND_LONG_RESOURCE_NAME
111+
.substring(0, MAX_ALLOWED_VOLUME_NAME_LENGTH - SECRET_VOLUME_SUFFIX2.length()) + SECRET_VOLUME_SUFFIX2;
112+
public static final String VOLUME_NAME_FOR_LONG_CONFIG_MAP_NAME = LONG_RESOURCE_NAME
113+
.substring(0, MAX_ALLOWED_VOLUME_NAME_LENGTH - SECRET_VOLUME_SUFFIX1.length()) + CM_VOLUME_SUFFIX1;
114+
public static final int MODE_420 = 420;
115+
public static final int MODE_365 = 365;
95116
private Method getDomainSpec;
96117
private final Domain domain = createTestDomain();
97118
private final DomainPresenceInfo domainPresenceInfo = createDomainPresenceInfo(domain);
@@ -462,6 +483,14 @@ private List<V1VolumeMount> getJobVolumeMounts() {
462483
.getVolumeMounts();
463484
}
464485

486+
private List<V1Volume> getJobVolumes() {
487+
return Optional.ofNullable(job.getSpec())
488+
.map(V1JobSpec::getTemplate)
489+
.map(V1PodTemplateSpec::getSpec)
490+
.map(V1PodSpec::getVolumes)
491+
.orElseThrow();
492+
}
493+
465494
@Test
466495
public void whenDomainHasAdditionalVolumesWithCustomVariables_createIntrospectorPodWithSubstitutions() {
467496
resourceLookup.defineResource(SECRET_NAME, KubernetesResourceType.Secret, NS);
@@ -500,6 +529,72 @@ public void whenDomainHasAdditionalVolumesWithCustomVariablesInvalidValue_jobNot
500529
assertThat(job, is(nullValue()));
501530
}
502531

532+
@Test
533+
public void whenDomainHasMultipleConfigOverrideSecretsWithLongNames_volumesCreatedWithShorterNames() {
534+
resourceLookup.defineResource(LONG_RESOURCE_NAME, KubernetesResourceType.Secret, NS);
535+
resourceLookup.defineResource(SECOND_LONG_RESOURCE_NAME, KubernetesResourceType.Secret, NS);
536+
537+
configureDomain()
538+
.withConfigOverrideSecrets(LONG_RESOURCE_NAME, SECOND_LONG_RESOURCE_NAME);
539+
540+
runCreateJob();
541+
542+
assertThat(getJobVolumes(), hasSecretVolume(VOLUME_NAME_FOR_LONG_SECRET_NAME, LONG_RESOURCE_NAME, MODE_420));
543+
assertThat(getJobVolumes(), hasSecretVolume(VOLUME_NAME_FOR_SECOND_LONG_SECRET_NAME,
544+
SECOND_LONG_RESOURCE_NAME, MODE_420));
545+
assertThat(getJobVolumeMounts(), hasVolumeMount(VOLUME_NAME_FOR_LONG_SECRET_NAME,
546+
"/weblogic-operator/config-overrides-secrets/" + LONG_RESOURCE_NAME, true));
547+
assertThat(getJobVolumeMounts(), hasVolumeMount(VOLUME_NAME_FOR_SECOND_LONG_SECRET_NAME,
548+
"/weblogic-operator/config-overrides-secrets/" + SECOND_LONG_RESOURCE_NAME, true));
549+
}
550+
551+
@Test
552+
public void whenDomainHasConfigMapOverrideWithLongConfigMapName_volumeCreatedWithShorterName() {
553+
resourceLookup.defineResource(LONG_RESOURCE_NAME, KubernetesResourceType.ConfigMap, NS);
554+
555+
configureDomain()
556+
.withConfigOverrides(LONG_RESOURCE_NAME);
557+
558+
runCreateJob();
559+
560+
assertThat(getJobVolumes(), hasConfigMapVolume(VOLUME_NAME_FOR_LONG_CONFIG_MAP_NAME, LONG_RESOURCE_NAME, MODE_365));
561+
assertThat(getJobVolumeMounts(), hasVolumeMount(VOLUME_NAME_FOR_LONG_CONFIG_MAP_NAME,
562+
"/weblogic-operator/config-overrides", true));
563+
}
564+
565+
@Test
566+
public void whenDomainHasModelConfigMapOverrideWithLongModelCMName_volumeCreatedWithShorterName() {
567+
resourceLookup.defineResource(LONG_RESOURCE_NAME, KubernetesResourceType.ConfigMap, NS);
568+
569+
configureDomain()
570+
.withDomainHomeSourceType(DomainSourceType.FromModel)
571+
.withModelConfigMap(LONG_RESOURCE_NAME);
572+
573+
runCreateJob();
574+
575+
assertThat(getJobVolumes(), hasConfigMapVolume(VOLUME_NAME_FOR_LONG_CONFIG_MAP_NAME, LONG_RESOURCE_NAME, MODE_365));
576+
assertThat(getJobVolumeMounts(), hasVolumeMount(VOLUME_NAME_FOR_LONG_CONFIG_MAP_NAME,
577+
"/weblogic-operator/wdt-config-map", true));
578+
}
579+
580+
@Test
581+
public void whenDomainHasMultipleConfigOverrideSecretsWithLongAndShortNames_volumeCreatedWithCorrectNames() {
582+
resourceLookup.defineResource(SECRET_NAME, KubernetesResourceType.Secret, NS);
583+
resourceLookup.defineResource(LONG_RESOURCE_NAME, KubernetesResourceType.Secret, NS);
584+
585+
configureDomain()
586+
.withConfigOverrideSecrets(SECRET_NAME, LONG_RESOURCE_NAME);
587+
588+
runCreateJob();
589+
590+
assertThat(getJobVolumes(), hasSecretVolume(SECRET_NAME + "-volume", SECRET_NAME, MODE_420));
591+
assertThat(getJobVolumes(), hasSecretVolume(VOLUME_NAME_FOR_LONG_SECRET_NAME, LONG_RESOURCE_NAME, MODE_420));
592+
assertThat(getJobVolumeMounts(), hasVolumeMount(SECRET_NAME + "-volume",
593+
"/weblogic-operator/config-overrides-secrets/" + SECRET_NAME, true));
594+
assertThat(getJobVolumeMounts(), hasVolumeMount(VOLUME_NAME_FOR_LONG_SECRET_NAME,
595+
"/weblogic-operator/config-overrides-secrets/" + LONG_RESOURCE_NAME, true));
596+
}
597+
503598
@Test
504599
public void verify_introspectorPodSpec_activeDeadlineSeconds_initial_values() {
505600
V1JobSpec jobSpec = createJobSpec();

operator/src/test/java/oracle/kubernetes/operator/helpers/Matchers.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.kubernetes.client.openapi.models.V1HostPathVolumeSource;
2020
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource;
2121
import io.kubernetes.client.openapi.models.V1Probe;
22+
import io.kubernetes.client.openapi.models.V1SecretVolumeSource;
2223
import io.kubernetes.client.openapi.models.V1Volume;
2324
import io.kubernetes.client.openapi.models.V1VolumeMount;
2425
import org.hamcrest.Description;
@@ -68,10 +69,24 @@ static Matcher<Iterable<? super V1VolumeMount>> hasVolumeMount(String name, Stri
6869
return hasItem(new V1VolumeMount().name(name).mountPath(path));
6970
}
7071

72+
static Matcher<Iterable<? super V1VolumeMount>> hasVolumeMount(String name, String path, boolean readOnly) {
73+
return hasItem(new V1VolumeMount().name(name).mountPath(path).readOnly(readOnly));
74+
}
75+
7176
static Matcher<Iterable<? super V1Volume>> hasVolume(String name, String path) {
7277
return hasItem(new V1Volume().name(name).hostPath(new V1HostPathVolumeSource().path(path)));
7378
}
7479

80+
static Matcher<Iterable<? super V1Volume>> hasSecretVolume(String name, String secretName, Integer defaultMode) {
81+
return hasItem(new V1Volume().name(name).secret(new V1SecretVolumeSource()
82+
.secretName(secretName).defaultMode(defaultMode)));
83+
}
84+
85+
static Matcher<Iterable<? super V1Volume>> hasConfigMapVolume(String name, String cmName, Integer defaultMode) {
86+
return hasItem(new V1Volume().name(name).configMap(new V1ConfigMapVolumeSource().name(cmName)
87+
.defaultMode(defaultMode)));
88+
}
89+
7590
static Matcher<Iterable<? super V1Volume>> hasPvClaimVolume(String name, String claimName) {
7691
return hasItem(new V1Volume().name(name).persistentVolumeClaim(
7792
new V1PersistentVolumeClaimVolumeSource().claimName(claimName)));

0 commit comments

Comments
 (0)