Skip to content

Commit 604c987

Browse files
authored
OWLS-80038 and OWLS-80090: fix mountPath validation and token substitution (#1911)
* Skip volume mount path validation if it contains valid tokens * Check admin serverPod's additional volume mount paths too in domain validation * Check cluster serverPod's additional mount paths too in domain validation * check mountpath validation after token substitution is performed * cleanup * fine tuning * minor cleanup * In progress * WIP * minor change * Modify a test name
1 parent 895c51d commit 604c987

16 files changed

+559
-83
lines changed

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.kubernetes.client.openapi.models.V1ConfigMap;
1111
import io.kubernetes.client.openapi.models.V1ConfigMapList;
1212
import io.kubernetes.client.openapi.models.V1ObjectMeta;
13+
import io.kubernetes.client.openapi.models.V1PodSpec;
1314
import io.kubernetes.client.openapi.models.V1Secret;
1415
import io.kubernetes.client.openapi.models.V1SecretList;
1516
import oracle.kubernetes.operator.DomainStatusUpdater;
@@ -43,6 +44,10 @@ public static Step createDomainValidationSteps(String namespace, Step next) {
4344
new DomainValidationStep(next));
4445
}
4546

47+
public static Step createAdditionalDomainValidationSteps(V1PodSpec podSpec) {
48+
return new DomainAdditionalValidationStep(podSpec);
49+
}
50+
4651
private static Step createListSecretsStep(String domainNamespace) {
4752
return new CallBuilder().listSecretsAsync(domainNamespace, new ListSecretsResponseStep());
4853
}
@@ -102,6 +107,34 @@ private String perLine(List<String> validationFailures) {
102107

103108
}
104109

110+
static class DomainAdditionalValidationStep extends Step {
111+
V1PodSpec podSpec;
112+
113+
DomainAdditionalValidationStep(V1PodSpec podSpec) {
114+
this.podSpec = podSpec;
115+
}
116+
117+
@Override
118+
public NextAction apply(Packet packet) {
119+
DomainPresenceInfo info = packet.getSpi(DomainPresenceInfo.class);
120+
Domain domain = info.getDomain();
121+
List<String> validationFailures = domain.getAdditionalValidationFailures(podSpec);
122+
123+
if (validationFailures.isEmpty()) {
124+
return doNext(packet);
125+
}
126+
127+
LOGGER.severe(DOMAIN_VALIDATION_FAILED, domain.getDomainUid(), perLine(validationFailures));
128+
Step step = DomainStatusUpdater.createFailedStep(BAD_DOMAIN, perLine(validationFailures), null);
129+
return doNext(step, packet);
130+
}
131+
132+
private String perLine(List<String> validationFailures) {
133+
return String.join(lineSeparator(), validationFailures);
134+
}
135+
136+
}
137+
105138
static class ValidateDomainTopologyStep extends Step {
106139

107140
ValidateDomainTopologyStep(Step next) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ public NextAction apply(Packet packet) {
346346

347347
return doNext(
348348
Step.chain(
349+
DomainValidationSteps.createAdditionalDomainValidationSteps(
350+
context.getJobModel().getSpec().getTemplate().getSpec()),
349351
createProgressingStep(info, INSPECTING_DOMAIN_PROGRESS_REASON, true, null),
350352
context.createNewJob(null),
351353
readDomainIntrospectorPodLogStep(null),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void init() {
6262

6363
// ------------------------ data methods ----------------------------
6464

65-
private V1Job getJobModel() {
65+
protected V1Job getJobModel() {
6666
return jobModel;
6767
}
6868

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ private void setRecordedPod(V1Pod pod) {
267267
* @return a step to be scheduled.
268268
*/
269269
Step verifyPod(Step next) {
270-
return new VerifyPodStep(next);
270+
return Step.chain(
271+
DomainValidationSteps.createAdditionalDomainValidationSteps(podModel.getSpec()),
272+
new VerifyPodStep(next));
271273
}
272274

273275
/**

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,20 @@
1717
import oracle.kubernetes.operator.Pair;
1818
import oracle.kubernetes.weblogic.domain.model.Domain;
1919

20+
import static oracle.kubernetes.weblogic.domain.model.Domain.TOKEN_END_MARKER;
21+
2022
public abstract class StepContextBase implements StepContextConstants {
2123
protected final DomainPresenceInfo info;
2224

2325
StepContextBase(DomainPresenceInfo info) {
2426
this.info = info;
2527
}
2628

27-
protected <T> T doDeepSubstitution(final Map<String, String> substitutionVariables, T obj) {
29+
<T> T doDeepSubstitution(final Map<String, String> substitutionVariables, T obj) {
2830
return doDeepSubstitution(substitutionVariables, obj, false);
2931
}
3032

31-
protected <T> T doDeepSubstitution(final Map<String, String> substitutionVariables, T obj, boolean requiresDns1123) {
33+
private <T> T doDeepSubstitution(final Map<String, String> substitutionVariables, T obj, boolean requiresDns1123) {
3234
if (obj instanceof String) {
3335
return (T) translate(substitutionVariables, (String) obj, requiresDns1123);
3436
} else if (obj instanceof List) {
@@ -74,7 +76,7 @@ protected <T> T doDeepSubstitution(final Map<String, String> substitutionVariabl
7476
return obj;
7577
}
7678

77-
boolean isDns1123Required(Method method) {
79+
private boolean isDns1123Required(Method method) {
7880
// value requires to be in DNS1123 if the value is for a name, which is assumed to be
7981
// name for a kubernetes object
8082
return LegalNames.isDns1123Required(method.getName().substring(3));
@@ -121,8 +123,8 @@ private String translate(final Map<String, String> substitutionVariables, String
121123
private String translate(final Map<String, String> substitutionVariables, String rawValue, boolean requiresDns1123) {
122124
String result = rawValue;
123125
for (Map.Entry<String, String> entry : substitutionVariables.entrySet()) {
124-
if (result != null && entry.getValue() != null) {
125-
result = result.replace(String.format("$(%s)", entry.getKey()),
126+
if (result != null && result.contains(Domain.TOKEN_START_MARKER) && entry.getValue() != null) {
127+
result = result.replace(String.format("%s%s%s", Domain.TOKEN_START_MARKER, entry.getKey(), TOKEN_END_MARKER),
126128
requiresDns1123 ? LegalNames.toDns1123LegalName(entry.getValue()) : entry.getValue());
127129
}
128130
}

operator/src/main/java/oracle/kubernetes/weblogic/domain/AdminServerConfigurator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33

44
package oracle.kubernetes.weblogic.domain;
55

6+
import oracle.kubernetes.weblogic.domain.model.AdminServer;
67
import oracle.kubernetes.weblogic.domain.model.AdminService;
78

89
@SuppressWarnings("UnusedReturnValue")
910
public interface AdminServerConfigurator extends ServerConfigurator {
1011

1112
AdminService configureAdminService();
13+
14+
AdminServer getAdminServer();
1215
}

operator/src/main/java/oracle/kubernetes/weblogic/domain/model/Domain.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.List;
1212
import java.util.Optional;
1313
import java.util.Set;
14+
import java.util.StringTokenizer;
1415
import java.util.function.Predicate;
1516
import java.util.stream.Collectors;
1617
import javax.annotation.Nonnull;
@@ -23,6 +24,7 @@
2324
import io.kubernetes.client.openapi.models.V1EnvVar;
2425
import io.kubernetes.client.openapi.models.V1LocalObjectReference;
2526
import io.kubernetes.client.openapi.models.V1ObjectMeta;
27+
import io.kubernetes.client.openapi.models.V1PodSpec;
2628
import io.kubernetes.client.openapi.models.V1SecretReference;
2729
import io.kubernetes.client.openapi.models.V1VolumeMount;
2830
import oracle.kubernetes.json.Description;
@@ -35,10 +37,22 @@
3537
import org.apache.commons.lang3.builder.HashCodeBuilder;
3638
import org.apache.commons.lang3.builder.ToStringBuilder;
3739

40+
import static java.util.stream.Collectors.toSet;
41+
3842
/**
3943
* Domain represents a WebLogic domain and how it will be realized in the Kubernetes cluster.
4044
*/
4145
public class Domain implements KubernetesObject {
46+
/**
47+
* The starting marker of a token that needs to be substituted with a matching env var.
48+
*/
49+
public static final String TOKEN_START_MARKER = "$(";
50+
51+
/**
52+
* The ending marker of a token that needs to be substituted with a matching env var.
53+
*/
54+
public static final String TOKEN_END_MARKER = ")";
55+
4256
/**
4357
* The pattern for computing the default shared logs directory.
4458
*/
@@ -595,6 +609,10 @@ public List<String> getValidationFailures(KubernetesResourceLookup kubernetesRes
595609
return new Validator().getValidationFailures(kubernetesResources);
596610
}
597611

612+
public List<String> getAdditionalValidationFailures(V1PodSpec podSpec) {
613+
return new Validator().getAdditionalValidationFailures(podSpec);
614+
}
615+
598616
class Validator {
599617
private final List<String> failures = new ArrayList<>();
600618
private final Set<String> clusterNames = new HashSet<>();
@@ -614,6 +632,11 @@ List<String> getValidationFailures(KubernetesResourceLookup kubernetesResources)
614632
return failures;
615633
}
616634

635+
public List<String> getAdditionalValidationFailures(V1PodSpec podSpec) {
636+
addInvalidMountPathsForPodSpec(podSpec);
637+
return failures;
638+
}
639+
617640
private void addDuplicateNames() {
618641
getSpec().getManagedServers()
619642
.stream()
@@ -655,14 +678,60 @@ private void checkDuplicateClusterName(String clusterName) {
655678

656679
private void addInvalidMountPaths() {
657680
getSpec().getAdditionalVolumeMounts().forEach(this::checkValidMountPath);
681+
if (getSpec().getAdminServer() != null) {
682+
getSpec().getAdminServer().getAdditionalVolumeMounts().forEach(this::checkValidMountPath);
683+
}
684+
if (getSpec().getClusters() != null) {
685+
getSpec().getClusters().forEach(
686+
cluster -> cluster.getAdditionalVolumeMounts().forEach(this::checkValidMountPath));
687+
}
688+
}
689+
690+
private void addInvalidMountPathsForPodSpec(V1PodSpec podSpec) {
691+
podSpec.getContainers()
692+
.forEach(container ->
693+
Optional.ofNullable(container.getVolumeMounts())
694+
.ifPresent(volumes -> volumes.forEach(this::checkValidMountPath)));
658695
}
659696

660697
private void checkValidMountPath(V1VolumeMount mount) {
698+
if (skipValidation(mount.getMountPath())) {
699+
return;
700+
}
701+
661702
if (!new File(mount.getMountPath()).isAbsolute()) {
662703
failures.add(DomainValidationMessages.badVolumeMountPath(mount));
663704
}
664705
}
665706

707+
private boolean skipValidation(String mountPath) {
708+
List<V1EnvVar> envVars = spec.getEnv();
709+
Set<String> varNames = envVars.stream().map(V1EnvVar::getName).collect(toSet());
710+
StringTokenizer nameList = new StringTokenizer(mountPath, TOKEN_START_MARKER);
711+
if (!nameList.hasMoreElements()) {
712+
return false;
713+
}
714+
while (nameList.hasMoreElements()) {
715+
String token = nameList.nextToken();
716+
if (noMatchingEnvVarName(varNames, token)) {
717+
return false;
718+
}
719+
}
720+
return true;
721+
}
722+
723+
private boolean noMatchingEnvVarName(Set<String> varNames, String token) {
724+
int index = token.indexOf(TOKEN_END_MARKER);
725+
if (index != -1) {
726+
String str = token.substring(0, index);
727+
// IntrospectorJobEnvVars.isReserved() checks env vars in ServerEnvVars too
728+
if (varNames.contains(str) || IntrospectorJobEnvVars.isReserved(str)) {
729+
return false;
730+
}
731+
}
732+
return true;
733+
}
734+
666735
private void addUnmappedLogHome() {
667736
if (!isLogHomeEnabled()) {
668737
return;

operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class DomainCommonConfigurator extends DomainConfigurator {
2929
public DomainCommonConfigurator() {
3030
}
3131

32-
DomainCommonConfigurator(@Nonnull Domain domain) {
32+
public DomainCommonConfigurator(@Nonnull Domain domain) {
3333
super(domain);
3434
setApiVersion(domain);
3535
}
@@ -386,6 +386,10 @@ class AdminServerConfiguratorImpl extends ServerConfiguratorImpl
386386
public AdminService configureAdminService() {
387387
return adminServer.createAdminService();
388388
}
389+
390+
public AdminServer getAdminServer() {
391+
return adminServer;
392+
}
389393
}
390394

391395
class ServerConfiguratorImpl implements ServerConfigurator {

operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobEnvVars.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ public class IntrospectorJobEnvVars {
3535
*/
3636
public static final String OPSS_WALLETFILE_SECRET_NAME = "OPSS_WALLETFILE_SECRET_NAME";
3737

38-
3938
/**
4039
* The credentials used by the introspection job - wdt encryption passphrase.
4140
*/
@@ -46,7 +45,6 @@ public class IntrospectorJobEnvVars {
4645
*/
4746
public static final String RUNTIME_ENCRYPTION_SECRET_NAME = "RUNTIME_ENCRYPTION_SECRET_NAME";
4847

49-
5048
/**
5149
* The domain source type.
5250
*/
@@ -83,5 +81,8 @@ static boolean isReserved(String name) {
8381
return ServerEnvVars.isReserved(name) || RESERVED_NAMES.contains(name);
8482
}
8583

86-
private static final List<String> RESERVED_NAMES = Arrays.asList(NAMESPACE, INTROSPECT_HOME, CREDENTIALS_SECRET_NAME);
84+
private static final List<String> RESERVED_NAMES = Arrays.asList(
85+
NAMESPACE, INTROSPECT_HOME, CREDENTIALS_SECRET_NAME, OPSS_KEY_SECRET_NAME, OPSS_WALLETFILE_SECRET_NAME,
86+
RUNTIME_ENCRYPTION_SECRET_NAME, WDT_DOMAIN_TYPE, DOMAIN_SOURCE_TYPE, ISTIO_ENABLED, ISTIO_READINESS_PORT,
87+
ISTIO_POD_NAMESPACE, WDT_MODEL_HOME);
8788
}

operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerEnvVars.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ public class ServerEnvVars {
6464

6565
private static final List<String> RESERVED_NAMES = Arrays.asList(
6666
DOMAIN_UID, DOMAIN_NAME, DOMAIN_HOME, NODEMGR_HOME, SERVER_NAME, SERVICE_NAME,
67-
ADMIN_NAME, AS_SERVICE_NAME, ADMIN_PORT, ADMIN_PORT_SECURE,
68-
LOG_HOME, SERVER_OUT_IN_POD_LOG);
67+
ADMIN_NAME, AS_SERVICE_NAME, ADMIN_PORT, ADMIN_PORT_SECURE, ADMIN_SERVER_PORT_SECURE,
68+
LOG_HOME, SERVER_OUT_IN_POD_LOG, DATA_HOME, ACCESS_LOG_IN_LOG_HOME, DYNAMIC_CONFIG_OVERRIDE);
6969

7070
static boolean isReserved(String name) {
7171
return RESERVED_NAMES.contains(name);

0 commit comments

Comments
 (0)