Skip to content

Commit 4892c3f

Browse files
authored
Verify shutdown rules when shutdown options are defined at different domain levels (#1870)
* test for ItPodsShutdown * remote JUnit4 ItPodsShutdown * address the review comment * refactor the code * add javadoc * rename the test class name as ItPodsShutdownOption
1 parent 294e0f6 commit 4892c3f

File tree

1 file changed

+291
-0
lines changed

1 file changed

+291
-0
lines changed
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
// Copyright (c) 2020, Oracle Corporation 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.weblogic.kubernetes;
5+
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Objects;
9+
import java.util.concurrent.TimeUnit;
10+
11+
import io.kubernetes.client.openapi.ApiException;
12+
import io.kubernetes.client.openapi.models.V1EnvVar;
13+
import io.kubernetes.client.openapi.models.V1LocalObjectReference;
14+
import io.kubernetes.client.openapi.models.V1ObjectMeta;
15+
import io.kubernetes.client.openapi.models.V1Pod;
16+
import io.kubernetes.client.openapi.models.V1SecretReference;
17+
import oracle.weblogic.domain.AdminServer;
18+
import oracle.weblogic.domain.Cluster;
19+
import oracle.weblogic.domain.Configuration;
20+
import oracle.weblogic.domain.Domain;
21+
import oracle.weblogic.domain.DomainSpec;
22+
import oracle.weblogic.domain.ManagedServer;
23+
import oracle.weblogic.domain.Model;
24+
import oracle.weblogic.domain.ServerPod;
25+
import oracle.weblogic.domain.Shutdown;
26+
import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes;
27+
import oracle.weblogic.kubernetes.annotations.IntegrationTest;
28+
import oracle.weblogic.kubernetes.annotations.Namespaces;
29+
import oracle.weblogic.kubernetes.logging.LoggingFacade;
30+
import org.junit.jupiter.api.BeforeAll;
31+
import org.junit.jupiter.api.DisplayName;
32+
import org.junit.jupiter.api.Test;
33+
34+
import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE;
35+
import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION;
36+
import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE;
37+
import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME;
38+
import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG;
39+
import static oracle.weblogic.kubernetes.TestConstants.REPO_SECRET_NAME;
40+
import static oracle.weblogic.kubernetes.TestConstants.WLS_DOMAIN_TYPE;
41+
import static oracle.weblogic.kubernetes.actions.TestActions.scaleCluster;
42+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodExists;
43+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReady;
44+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists;
45+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createDockerRegistrySecret;
46+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createDomainAndVerify;
47+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createSecretWithUsernamePassword;
48+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.dockerLoginAndPushImageToRegistry;
49+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.installAndVerifyOperator;
50+
import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger;
51+
import static org.assertj.core.api.Assertions.assertThat;
52+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
53+
import static org.junit.jupiter.api.Assertions.assertNotNull;
54+
import static org.junit.jupiter.api.Assertions.assertTrue;
55+
56+
/**
57+
* This test is to verify shutdown rules when shutdown properties are defined at different levels
58+
* (domain, cluster, adminServer and managedServer level).
59+
*/
60+
@DisplayName("Verify shutdown rules when shutdown properties are defined at different levels")
61+
@IntegrationTest
62+
class ItPodsShutdown {
63+
64+
private static String domainNamespace = null;
65+
66+
// domain constants
67+
private static final String domainUid = "domain1";
68+
private static final String clusterName = "cluster-1";
69+
private static final int replicaCount = 1;
70+
private static final String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE;
71+
private static final String managedServerPrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE;
72+
private static final String managedServer1Name = "managed-server1";
73+
private static LoggingFacade logger = null;
74+
75+
/**
76+
* Get namespaces for operator and WebLogic domain.
77+
*
78+
* @param namespaces list of namespaces created by the IntegrationTestWatcher by the
79+
* JUnit engine parameter resolution mechanism
80+
*/
81+
@BeforeAll
82+
public static void initAll(@Namespaces(2) List<String> namespaces) {
83+
logger = getLogger();
84+
// get a unique operator namespace
85+
logger.info("Getting a unique namespace for operator");
86+
assertNotNull(namespaces.get(0), "Namespace list is null");
87+
String opNamespace = namespaces.get(0);
88+
89+
// get a unique domain namespace
90+
logger.info("Getting a unique namespace for WebLogic domain");
91+
assertNotNull(namespaces.get(1), "Namespace list is null");
92+
domainNamespace = namespaces.get(1);
93+
94+
// install and verify operator
95+
installAndVerifyOperator(opNamespace, domainNamespace);
96+
97+
// create a basic model in image domain
98+
createAndVerifyMiiDomain();
99+
}
100+
101+
/**
102+
* This test is to verify different shutdown options specified at different scopes in Domain Resource Definition.
103+
* Refer to section "Shutdown options" of Documentation link:
104+
* https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-lifecycle/startup/
105+
* step 1: Startup a WebLogic domain with one cluster that initially has one running managed server. The shutdown option is
106+
* configured as follows:
107+
* domain: SHUTDOWN_TYPE -> Graceful.
108+
* adminServer: SHUTDOWN_TYPE -> Forced.
109+
* SHUTDOWN_IGNORE_SESSIONS -> true.
110+
* SHUTDOWN_TIMEOUT -> 40.
111+
* cluster: SHUTDOWN_IGNORE_SESSIONS -> true.
112+
* SHUTDOWN_TIMEOUT -> 80.
113+
* managedServer1: SHUTDOWN_TIMEOUT -> 100.
114+
* step2: Scale cluster with two managed servers.
115+
* step 3: Verify shutdown properties of admin server, managedServer1 and newly scaled up managedServer2.
116+
* Domain level "Graceful" SHUTDOWN_TYPE overrides server level setting and is used for all servers.
117+
* So adminServer has "Graceful" SHUTDOWN_TYPE, default "false" SHUTDOWN_IGNORE_SESSIONS and
118+
* configured "40" SHUTDOWN_TIMEOUT.
119+
* Managed level setting overrides cluster level setting so managedServer1 has configured "100"
120+
* SHUTDOWN_TIMEOUT, cluster level "true" SHUTDOWN_IGNORE_SESSIONS "true" and "Graceful" SHUTDOWN_TYPE.
121+
* Newly scaled up managedServer2 takes setting from combination of domain level and cluster level
122+
* with "Graceful" SHUTDOWN_TYPE, "true" SHUTDOWN_IGNORE_SESSIONS and default "30" SHUTDOWN_TIMEOUT
123+
*/
124+
@Test
125+
@DisplayName("Verify shutdown rules when shutdown properties are defined at different levels ")
126+
public void testShutdownProps() throws ApiException {
127+
128+
//scale the cluster to 2 managed servers
129+
assertThat(assertDoesNotThrow(() -> scaleCluster(domainUid, domainNamespace, clusterName, 2)))
130+
.as("Verify scaling cluster {0} of domain {1} in namespace {2} succeeds",
131+
clusterName, domainUid, domainNamespace)
132+
.withFailMessage("Scaling cluster {0} of domain {1} in namespace {2} failed",
133+
clusterName, domainUid, domainNamespace)
134+
.isTrue();
135+
assertDoesNotThrow(() -> TimeUnit.SECONDS.sleep(30));
136+
137+
assertTrue(verifyServerShutdownProp(adminServerPodName, domainNamespace, "Graceful", "40", "false"));
138+
assertTrue(verifyServerShutdownProp(managedServerPrefix + 1, domainNamespace, "Graceful", "100", "true"));
139+
assertTrue(verifyServerShutdownProp(managedServerPrefix + 2, domainNamespace, "Graceful", "30", "true"));
140+
}
141+
142+
/**
143+
* Create a model in image domain and verify the server pods are ready.
144+
*/
145+
private static void createAndVerifyMiiDomain() {
146+
147+
// get the pre-built image created by IntegrationTestWatcher
148+
String miiImage = MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG;
149+
150+
// docker login and push image to docker registry if necessary
151+
dockerLoginAndPushImageToRegistry(miiImage);
152+
153+
// create docker registry secret to pull the image from registry
154+
logger.info("Creating docker registry secret in namespace {0}", domainNamespace);
155+
createDockerRegistrySecret(domainNamespace);
156+
157+
// create secret for admin credentials
158+
logger.info("Creating secret for admin credentials");
159+
String adminSecretName = "weblogic-credentials";
160+
createSecretWithUsernamePassword(adminSecretName, domainNamespace, "weblogic", "welcome1");
161+
162+
// create encryption secret
163+
logger.info("Creating encryption secret");
164+
String encryptionSecretName = "encryptionsecret";
165+
createSecretWithUsernamePassword(encryptionSecretName, domainNamespace, "weblogicenc", "weblogicenc");
166+
167+
// create the domain CR
168+
Domain domain = new Domain()
169+
.apiVersion(DOMAIN_API_VERSION)
170+
.kind("Domain")
171+
.metadata(new V1ObjectMeta()
172+
.name(domainUid)
173+
.namespace(domainNamespace))
174+
.spec(new DomainSpec()
175+
.domainUid(domainUid)
176+
.domainHomeSourceType("FromModel")
177+
.image(miiImage)
178+
.addImagePullSecretsItem(new V1LocalObjectReference()
179+
.name(REPO_SECRET_NAME))
180+
.webLogicCredentialsSecret(new V1SecretReference()
181+
.name(adminSecretName)
182+
.namespace(domainNamespace))
183+
.includeServerOutInPodLog(true)
184+
.serverStartPolicy("IF_NEEDED")
185+
.serverPod(new ServerPod()
186+
.addEnvItem(new V1EnvVar()
187+
.name("JAVA_OPTIONS")
188+
.value("-Dweblogic.StdoutDebugEnabled=false"))
189+
.addEnvItem(new V1EnvVar()
190+
.name("USER_MEM_ARGS")
191+
.value("-Djava.security.egd=file:/dev/./urandom "))
192+
.addEnvItem(new V1EnvVar()
193+
.name("SHUTDOWN_TYPE")
194+
.value("Graceful")))
195+
.adminServer(new AdminServer()
196+
.serverStartState("RUNNING")
197+
.serverPod(new ServerPod().shutdown(new Shutdown().shutdownType("Forced")))
198+
.serverPod(new ServerPod().shutdown(new Shutdown().ignoreSessions(true)))
199+
.serverPod(new ServerPod().shutdown(new Shutdown().timeoutSeconds(40L))))
200+
.addClustersItem(new Cluster()
201+
.clusterName(clusterName)
202+
.replicas(replicaCount)
203+
.serverStartState("RUNNING")
204+
.serverPod(new ServerPod().shutdown(new Shutdown().timeoutSeconds(80L)))
205+
.serverPod(new ServerPod().shutdown(new Shutdown().ignoreSessions(true))))
206+
.configuration(new Configuration()
207+
.model(new Model()
208+
.domainType(WLS_DOMAIN_TYPE)
209+
.runtimeEncryptionSecret(encryptionSecretName)))
210+
.addManagedServersItem(new ManagedServer()
211+
.serverName(managedServer1Name)
212+
.serverPod(new ServerPod().shutdown(new Shutdown().timeoutSeconds(100L)))));
213+
214+
// create model in image domain
215+
logger.info("Creating model in image domain {0} in namespace {1} using docker image {2}",
216+
domainUid, domainNamespace, miiImage);
217+
createDomainAndVerify(domain, domainNamespace);
218+
219+
// check that admin server pod exists in the domain namespace
220+
logger.info("Checking that admin server pod {0} exists in namespace {1}",
221+
adminServerPodName, domainNamespace);
222+
checkPodExists(adminServerPodName, domainUid, domainNamespace);
223+
224+
// check that admin server pod is ready
225+
logger.info("Checking that admin server pod {0} is ready in namespace {1}",
226+
adminServerPodName, domainNamespace);
227+
checkPodReady(adminServerPodName, domainUid, domainNamespace);
228+
229+
// check that admin service exists in the domain namespace
230+
logger.info("Checking that admin service {0} exists in namespace {1}",
231+
adminServerPodName, domainNamespace);
232+
checkServiceExists(adminServerPodName, domainNamespace);
233+
234+
// check for managed server pods existence in the domain namespace
235+
for (int i = 1; i <= replicaCount; i++) {
236+
String managedServerPodName = managedServerPrefix + i;
237+
238+
// check that the managed server pod exists in the domain namespace
239+
logger.info("Checking that managed server pod {0} exists in namespace {1}",
240+
managedServerPodName, domainNamespace);
241+
checkPodExists(managedServerPodName, domainUid, domainNamespace);
242+
243+
// check that the managed server pod is ready
244+
logger.info("Checking that managed server pod {0} is ready in namespace {1}",
245+
managedServerPodName, domainNamespace);
246+
checkPodReady(managedServerPodName, domainUid, domainNamespace);
247+
248+
// check that the managed server service exists in the domain namespace
249+
logger.info("Checking that managed server service {0} exists in namespace {1}",
250+
managedServerPodName, domainNamespace);
251+
checkServiceExists(managedServerPodName, domainNamespace);
252+
}
253+
}
254+
255+
/**
256+
* Verify the server pod Shutdown properties.
257+
* @param podName the name of the server pod
258+
* @param domainNS the namespace where the server pod exist
259+
* @param props the shutdown properties
260+
*/
261+
private static boolean verifyServerShutdownProp(
262+
String podName,
263+
String domainNS,
264+
String... props) throws io.kubernetes.client.openapi.ApiException {
265+
266+
V1Pod serverPod = Kubernetes.getPod(domainNS, null, podName);
267+
assertNotNull(serverPod,"The server pod does not exist in namespace " + domainNS);
268+
List<V1EnvVar> envVars = Objects.requireNonNull(serverPod.getSpec()).getContainers().get(0).getEnv();
269+
270+
boolean found = false;
271+
HashMap<String, Boolean> propFound = new HashMap<String, Boolean>();
272+
for (String prop : props) {
273+
for (var envVar : envVars) {
274+
if (envVar.getName().contains("SHUTDOWN")) {
275+
if (envVar.getValue() != null && envVar.getValue().contains(prop)) {
276+
logger.info("For pod {0} SHUTDOWN option {1} has value {2} ",
277+
podName, envVar.getName(), envVar.getValue());
278+
logger.info("Property with value " + prop + " has found");
279+
propFound.put(prop, true);
280+
}
281+
}
282+
}
283+
}
284+
if (props.length == propFound.size()) {
285+
found = true;
286+
}
287+
return found;
288+
}
289+
290+
}
291+

0 commit comments

Comments
 (0)