Skip to content

Commit 8d9f820

Browse files
authored
Owls 91563 - Changes to validate the container port names and truncate automatically generated long names (#2542) (#2548)
* Changes to validate the container port names and truncate automatically generated long names
1 parent 5e86c59 commit 8d9f820

File tree

9 files changed

+169
-2
lines changed

9 files changed

+169
-2
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public class LegalNames {
5454
// The maximum length of a legal DNS label name
5555
public static final int LEGAL_DNS_LABEL_NAME_MAX_LENGTH = 63;
5656

57+
// The maximum length of a container port name
58+
public static final int LEGAL_CONTAINER_PORT_NAME_MAX_LENGTH = 15;
59+
5760
private static final String DNS_NAME_REGEXP = "[a-z0-9]([-a-z0-9]*[a-z0-9])?";
5861
private static final Pattern DNS_NAME_PATTERN = Pattern.compile(DNS_NAME_REGEXP);
5962

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import oracle.kubernetes.weblogic.domain.model.ServerSpec;
7474
import oracle.kubernetes.weblogic.domain.model.Shutdown;
7575
import org.apache.commons.lang3.builder.EqualsBuilder;
76+
import org.jetbrains.annotations.NotNull;
7677

7778
import static oracle.kubernetes.operator.EventConstants.ROLL_REASON_DOMAIN_RESOURCE_CHANGED;
7879
import static oracle.kubernetes.operator.EventConstants.ROLL_REASON_WEBLOGIC_CONFIGURATION_CHANGED;
@@ -91,6 +92,7 @@
9192
import static oracle.kubernetes.operator.helpers.CompatibilityCheck.CompatibilityScope.UNKNOWN;
9293
import static oracle.kubernetes.operator.helpers.EventHelper.EventItem.DOMAIN_ROLL_STARTING;
9394
import static oracle.kubernetes.operator.helpers.EventHelper.EventItem.POD_CYCLE_STARTING;
95+
import static oracle.kubernetes.operator.helpers.LegalNames.LEGAL_CONTAINER_PORT_NAME_MAX_LENGTH;
9496

9597
public abstract class PodStepContext extends BasePodStepContext {
9698

@@ -295,7 +297,7 @@ private boolean isSipProtocol(NetworkAccessPoint nap) {
295297
}
296298

297299
private void addContainerPort(List<V1ContainerPort> ports, NetworkAccessPoint nap) {
298-
String name = LegalNames.toDns1123LegalName(nap.getName());
300+
String name = createContainerPortName(ports, LegalNames.toDns1123LegalName(nap.getName()));
299301
addContainerPort(ports, name, nap.getListenPort(), "TCP");
300302

301303
if (isSipProtocol(nap)) {
@@ -306,10 +308,39 @@ private void addContainerPort(List<V1ContainerPort> ports, NetworkAccessPoint na
306308
private void addContainerPort(List<V1ContainerPort> ports, String name,
307309
@Nullable Integer listenPort, String protocol) {
308310
if (listenPort != null) {
311+
name = createContainerPortName(ports, name);
309312
ports.add(new V1ContainerPort().name(name).containerPort(listenPort).protocol(protocol));
310313
}
311314
}
312315

316+
private String createContainerPortName(List<V1ContainerPort> ports, String name) {
317+
//Container port names can be a maximum of 15 characters in length
318+
if (name.length() > LEGAL_CONTAINER_PORT_NAME_MAX_LENGTH) {
319+
String portNamePrefix = getPortNamePrefix(name);
320+
// Find ports with the name having the same first 12 characters
321+
List<V1ContainerPort> containerPortsWithSamePrefix = ports.stream().filter(port ->
322+
portNamePrefix.equals(getPortNamePrefix(port.getName()))).collect(Collectors.toList());
323+
int index = containerPortsWithSamePrefix.size() + 1;
324+
String indexStr = String.valueOf(index);
325+
// zero fill to the left for single digit index (e.g. 01)
326+
if (index < 10) {
327+
indexStr = "0" + index;
328+
} else if (index >= 100) {
329+
LOGGER.severe(MessageKeys.ILLEGAL_NETWORK_CHANNEL_NAME_LENGTH, getDomainUid(), getServerName(),
330+
name, LEGAL_CONTAINER_PORT_NAME_MAX_LENGTH);
331+
return name;
332+
}
333+
name = portNamePrefix + "-" + indexStr;
334+
}
335+
return name;
336+
}
337+
338+
@NotNull
339+
private String getPortNamePrefix(String name) {
340+
// Use first 12 characters of port name as prefix due to 15 character port name limit
341+
return name.substring(0, 12);
342+
}
343+
313344
Integer getListenPort() {
314345
return Optional.ofNullable(scan).map(WlsServerConfig::getListenPort).orElse(null);
315346
}

operator/src/main/java/oracle/kubernetes/operator/logging/MessageKeys.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ public class MessageKeys {
144144
public static final String LOG_WAITING_COUNT = "WLSKO-0193";
145145
public static final String INTERNAL_IDENTITY_INITIALIZATION_FAILED = "WLSKO-0194";
146146

147-
148147
// domain status messages
149148
public static final String DUPLICATE_SERVER_NAME_FOUND = "WLSDO-0001";
150149
public static final String DUPLICATE_CLUSTER_NAME_FOUND = "WLSDO-0002";
@@ -176,6 +175,8 @@ public class MessageKeys {
176175
public static final String MONITORING_EXPORTER_CONFLICT_DYNAMIC_CLUSTER = "WLSDO-0028";
177176
public static final String INVALID_LIVENESS_PROBE_SUCCESS_THRESHOLD_VALUE = "WLSDO-0029";
178177
public static final String RESERVED_CONTAINER_NAME = "WLSDO-0030";
178+
public static final String ILLEGAL_CONTAINER_PORT_NAME_LENGTH = "WLSDO-0031";
179+
public static final String ILLEGAL_NETWORK_CHANNEL_NAME_LENGTH = "WLSDO-0032";
179180

180181
private MessageKeys() {
181182
}

operator/src/main/java/oracle/kubernetes/operator/wlsconfig/WlsDomainConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,20 @@ public WlsDomainConfig withAdminServer(String adminServerName, String listenAddr
407407
return this;
408408
}
409409

410+
/**
411+
* Build the domain config with a WLS server.
412+
* @param server WLS server configuration
413+
* @param isAdmin Specifies if the server is Admin or managed server.
414+
* @return domain config
415+
*/
416+
public WlsDomainConfig withServer(WlsServerConfig server, boolean isAdmin) {
417+
if (isAdmin) {
418+
setAdminServerName(server.getName());
419+
}
420+
getServers().add(server);
421+
return this;
422+
}
423+
410424
public WlsDomainConfig addWlsServer(String name, String listenAddress, int port) {
411425
getServers().add(new WlsServerConfig(name, listenAddress, port));
412426
return this;

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.gson.annotations.SerializedName;
2222
import io.kubernetes.client.common.KubernetesObject;
2323
import io.kubernetes.client.openapi.models.V1Container;
24+
import io.kubernetes.client.openapi.models.V1ContainerPort;
2425
import io.kubernetes.client.openapi.models.V1EnvVar;
2526
import io.kubernetes.client.openapi.models.V1LocalObjectReference;
2627
import io.kubernetes.client.openapi.models.V1ObjectMeta;
@@ -833,6 +834,7 @@ List<String> getValidationFailures(KubernetesResourceLookup kubernetesResources)
833834
addDuplicateAuxiliaryImageVolumeNames();
834835
verifyLivenessProbeSuccessThreshold();
835836
verifyContainerNameValidInPodSpec();
837+
verifyContainerPortNameValidInPodSpec();
836838

837839
return failures;
838840
}
@@ -1118,6 +1120,33 @@ private void isContainerNameReserved(V1Container container, String prefix) {
11181120
}
11191121
}
11201122

1123+
private void verifyContainerPortNameValidInPodSpec() {
1124+
getAdminServerSpec().getContainers().forEach(container ->
1125+
areContainerPortNamesValid(container, ADMIN_SERVER_POD_SPEC_PREFIX + ".containers"));
1126+
getSpec().getClusters().forEach(cluster ->
1127+
cluster.getContainers().forEach(container ->
1128+
areContainerPortNamesValid(container, CLUSTER_SPEC_PREFIX + "[" + cluster.getClusterName()
1129+
+ "].serverPod.containers")));
1130+
getSpec().getManagedServers().forEach(managedServer ->
1131+
managedServer.getContainers().forEach(container ->
1132+
areContainerPortNamesValid(container, MS_SPEC_PREFIX + "[" + managedServer.getServerName()
1133+
+ "].serverPod.containers")));
1134+
}
1135+
1136+
private void areContainerPortNamesValid(V1Container container, String prefix) {
1137+
Optional.ofNullable(container.getPorts()).ifPresent(portList ->
1138+
portList.forEach(port -> checkPortNameLength(port, container.getName(), prefix)));
1139+
}
1140+
1141+
private void checkPortNameLength(V1ContainerPort port, String name, String prefix) {
1142+
if (port.getName().length() > LegalNames.LEGAL_CONTAINER_PORT_NAME_MAX_LENGTH) {
1143+
failures.add(DomainValidationMessages.exceedMaxContainerPortName(
1144+
getDomainUid(),
1145+
prefix + "." + name,
1146+
port.getName()));
1147+
}
1148+
}
1149+
11211150
@Nonnull
11221151
private Set<String> getEnvNames() {
11231152
return Optional.ofNullable(spec.getEnv()).stream()

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import oracle.kubernetes.operator.logging.MessageKeys;
1616
import oracle.kubernetes.utils.OperatorUtils;
1717

18+
import static oracle.kubernetes.operator.helpers.LegalNames.LEGAL_CONTAINER_PORT_NAME_MAX_LENGTH;
19+
1820
class DomainValidationMessages {
1921

2022
/**
@@ -174,4 +176,10 @@ public static String invalidLivenessProbeSuccessThresholdValue(int value, String
174176
public static String reservedContainerName(String name, String prefix) {
175177
return getMessage(MessageKeys.RESERVED_CONTAINER_NAME, name, prefix);
176178
}
179+
180+
public static String exceedMaxContainerPortName(String domainUid, String containerName, String portName) {
181+
return getMessage(MessageKeys.ILLEGAL_CONTAINER_PORT_NAME_LENGTH, domainUid, containerName, portName,
182+
LEGAL_CONTAINER_PORT_NAME_MAX_LENGTH);
183+
}
184+
177185
}

operator/src/main/resources/Operator.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ WLSDO-0027=The Monitoring Exporter port ''{0}'' specified by ''spec.monitoringEx
184184
WLSDO-0028=The Monitoring Exporter port ''{0}'' specified by ''spec.monitoringExporter.port'' conflicts with Cluster ''{1}'' dynamic server template port ''{2}''
185185
WLSDO-0029=Invalid value ''{0}'' for the liveness probe success threshold under ''{1}''. The liveness probe successThreshold value must be 1.
186186
WLSDO-0030=The container name ''{0}'' specified under ''{1}'' is reserved for use by the operator.
187+
WLSDO-0031=Container port name ''{2}'' for domain with domainUID ''{0}'' and container name ''{1}'' exceeds maximum allowed length ''{3}''.
188+
WLSDO-0032=Network channel name ''{2}'' for domain with domainUID ''{0}'' and server with \
189+
name ''{1}'' exceeds maximum allowed length ''{3}''. Please specify a shorter channel name.
187190

188191
oneEnvVar=variable
189192
multipleEnvVars=variables

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
import static org.hamcrest.Matchers.equalTo;
162162
import static org.hamcrest.Matchers.hasEntry;
163163
import static org.hamcrest.Matchers.hasItem;
164+
import static org.hamcrest.Matchers.hasItems;
164165
import static org.hamcrest.Matchers.hasKey;
165166
import static org.hamcrest.Matchers.hasSize;
166167
import static org.hamcrest.Matchers.is;
@@ -201,6 +202,8 @@ public abstract class PodHelperTestBase extends DomainValidationBaseTest {
201202
private static final int READ_AND_EXECUTE_MODE = 0555;
202203
private static final String TEST_PRODUCT_VERSION = "unit-test";
203204
private static final String NOOP_EXPORTER_CONFIG = "queries:\n";
205+
public static final String LONG_CHANNEL_NAME = "Very_Long_Channel_Name";
206+
public static final String TRUNCATED_PORT_NAME_PREFIX = "very-long-ch";
204207

205208
final TerminalStep terminalStep = new TerminalStep();
206209
private final Domain domain = createDomain();
@@ -378,6 +381,43 @@ void whenPodCreatedWithAdminNap_prometheusAnnotationsSpecifyPlainTextPort() {
378381
hasEntry("prometheus.io/scrape", "true")));
379382
}
380383

384+
@Test
385+
void whenPodCreatedWithAdminNapNameExceedingMaxPortNameLength_podContainerCreatedWithTruncatedPortName() {
386+
getServerTopology().addNetworkAccessPoint(new NetworkAccessPoint(LONG_CHANNEL_NAME, "admin", 8001, 8001));
387+
assertThat(
388+
getContainerPorts(),
389+
hasItem(createContainerPort(TRUNCATED_PORT_NAME_PREFIX + "-01")));
390+
}
391+
392+
private List<V1ContainerPort> getContainerPorts() {
393+
return getCreatedPod().getSpec().getContainers().stream()
394+
.filter(c -> c.getName().equals(WLS_CONTAINER_NAME)).findFirst().map(c -> c.getPorts()).orElse(null);
395+
}
396+
397+
private V1ContainerPort createContainerPort(String portName) {
398+
return new V1ContainerPort().name(portName).containerPort(8001).protocol("TCP");
399+
}
400+
401+
@Test
402+
void whenPodCreatedWithMultipleNapsWithNamesExceedingMaxPortNameLength_podContainerCreatedWithTruncatedPortNames() {
403+
getServerTopology().addNetworkAccessPoint(new NetworkAccessPoint(LONG_CHANNEL_NAME + "1", "admin", 8001, 8001));
404+
getServerTopology().addNetworkAccessPoint(new NetworkAccessPoint(LONG_CHANNEL_NAME + "11", "admin", 8001, 8001));
405+
assertThat(
406+
getContainerPorts(),
407+
hasItems(createContainerPort(TRUNCATED_PORT_NAME_PREFIX + "-01"),
408+
createContainerPort(TRUNCATED_PORT_NAME_PREFIX + "-02")));
409+
}
410+
411+
@Test
412+
void whenPodCreatedWithMultipleNapsSomeWithNamesExceedingMaxPortNameLength_podContainerCreatedWithMixedPortNames() {
413+
getServerTopology().addNetworkAccessPoint(new NetworkAccessPoint(LONG_CHANNEL_NAME, "admin", 8001, 8001));
414+
getServerTopology().addNetworkAccessPoint(new NetworkAccessPoint("My_Channel_Name", "admin", 8001, 8001));
415+
assertThat(
416+
getContainerPorts(),
417+
hasItems(createContainerPort(TRUNCATED_PORT_NAME_PREFIX + "-01"),
418+
createContainerPort("my-channel-name")));
419+
}
420+
381421
protected DomainConfigurator defineExporterConfiguration() {
382422
return configureDomain()
383423
.withMonitoringExporterConfiguration(NOOP_EXPORTER_CONFIG)

operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainValidationTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
package oracle.kubernetes.weblogic.domain.model;
55

66
import java.util.ArrayList;
7+
import java.util.Arrays;
78
import java.util.Collections;
89
import java.util.List;
910

1011
import com.meterware.simplestub.Memento;
1112
import io.kubernetes.client.openapi.models.V1Container;
13+
import io.kubernetes.client.openapi.models.V1ContainerPort;
1214
import io.kubernetes.client.openapi.models.V1LocalObjectReference;
1315
import oracle.kubernetes.operator.TuningParameters;
1416
import oracle.kubernetes.operator.helpers.KubernetesTestSupport;
@@ -50,6 +52,7 @@ class DomainValidationTest extends DomainValidationBaseTest {
5052
private static final String BAD_MOUNT_PATH_3 = "$()DOMAIN_HOME/servers/SERVER_NAME";
5153
public static final String TEST_VOLUME_NAME = "test";
5254
public static final String WRONG_VOLUME_NAME = "BadVolume";
55+
public static final String LONG_CONTAINER_PORT_NAME = "long-container-port-name";
5356

5457
private final Domain domain = createTestDomain();
5558
private final KubernetesTestSupport testSupport = new KubernetesTestSupport();
@@ -530,6 +533,41 @@ void whenReservedContainerNameUsedForManagedServer_reportError() {
530533
"is reserved", "operator")));
531534
}
532535

536+
@Test
537+
void whenContainerPortNameExceedsMaxLength_ForAdminServerContainer_reportError() {
538+
configureDomain(domain)
539+
.withContainer(new V1Container().name("Test")
540+
.ports(Arrays.asList(new V1ContainerPort().name(LONG_CONTAINER_PORT_NAME))));
541+
542+
assertThat(domain.getValidationFailures(resourceLookup), contains(stringContainsInOrder(
543+
"Container port name ", LONG_CONTAINER_PORT_NAME, "domainUID", UID, "adminServer", "Test",
544+
"exceeds maximum allowed length '15'")));
545+
}
546+
547+
@Test
548+
void whenContainerPortNameExceedsMaxLength_ForClusteredServerContainer_reportError() {
549+
configureDomain(domain)
550+
.configureCluster("cluster-1")
551+
.withContainer(new V1Container().name("Test")
552+
.ports(Arrays.asList(new V1ContainerPort().name(LONG_CONTAINER_PORT_NAME))));
553+
554+
assertThat(domain.getValidationFailures(resourceLookup), contains(stringContainsInOrder(
555+
"Container port name ", LONG_CONTAINER_PORT_NAME, "domainUID", UID, "cluster-1", "Test",
556+
"exceeds maximum allowed length '15'")));
557+
}
558+
559+
@Test
560+
void whenContainerPortNameExceedsMaxLength_ForManagedServerContainer_reportError() {
561+
configureDomain(domain)
562+
.configureServer("managed-server1")
563+
.withContainer(new V1Container().name("Test")
564+
.ports(Arrays.asList(new V1ContainerPort().name(LONG_CONTAINER_PORT_NAME))));
565+
566+
assertThat(domain.getValidationFailures(resourceLookup), contains(stringContainsInOrder(
567+
"Container port name ", LONG_CONTAINER_PORT_NAME, "domainUID", UID, "managed-server1", "Test",
568+
"exceeds maximum allowed length '15'")));
569+
}
570+
533571
@Test
534572
void whenWebLogicCredentialsSecretNameNotFound_reportError() {
535573
resourceLookup.undefineResource(SECRET_NAME, KubernetesResourceType.Secret, NS);

0 commit comments

Comments
 (0)