Skip to content

OWLS-87359 correctly handle a large number of secrets and configmaps #2199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package oracle.kubernetes.operator.helpers;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -16,6 +17,7 @@
import oracle.kubernetes.operator.DomainStatusUpdater;
import oracle.kubernetes.operator.MakeRightDomainOperation;
import oracle.kubernetes.operator.ProcessingConstants;
import oracle.kubernetes.operator.calls.AsyncRequestStep;
import oracle.kubernetes.operator.calls.CallResponse;
import oracle.kubernetes.operator.helpers.EventHelper.EventData;
import oracle.kubernetes.operator.helpers.EventHelper.EventItem;
Expand Down Expand Up @@ -47,7 +49,7 @@ public static Step createDomainValidationSteps(String namespace, Step next) {
new DomainValidationStep(next));
}

public static Step createAdditionalDomainValidationSteps(V1PodSpec podSpec) {
static Step createAdditionalDomainValidationSteps(V1PodSpec podSpec) {
return new DomainAdditionalValidationStep(podSpec);
}

Expand All @@ -59,18 +61,29 @@ private static Step createListSecretsStep(String domainNamespace) {
return new CallBuilder().listSecretsAsync(domainNamespace, new ListSecretsResponseStep());
}

public static Step createValidateDomainTopologyStep(Step next) {
static Step createValidateDomainTopologyStep(Step next) {
return new ValidateDomainTopologyStep(next);
}

static class ListSecretsResponseStep extends DefaultResponseStep<V1SecretList> {

@Override
public NextAction onSuccess(Packet packet, CallResponse<V1SecretList> callResponse) {
packet.put(SECRETS, callResponse.getResult().getItems());
List<V1Secret> list = getSecrets(packet);
list.addAll(callResponse.getResult().getItems());
packet.put(SECRETS, list);

return doContinueListOrNext(callResponse, packet);
}

static List<V1Secret> getSecrets(Packet packet) {
return Optional.ofNullable(getSecretsIfContinue(packet)).orElse(new ArrayList<>());
}

@SuppressWarnings("unchecked")
private static List<V1Secret> getSecretsIfContinue(Packet packet) {
return packet.get(AsyncRequestStep.CONTINUE) != null ? (List<V1Secret>) packet.get(SECRETS) : null;
}
}

private static Step createListConfigMapsStep(String domainNamespace) {
Expand All @@ -81,10 +94,21 @@ static class ListConfigMapsResponseStep extends DefaultResponseStep<V1ConfigMapL

@Override
public NextAction onSuccess(Packet packet, CallResponse<V1ConfigMapList> callResponse) {
packet.put(CONFIGMAPS, callResponse.getResult().getItems());
List<V1ConfigMap> list = getConfigMaps(packet);
list.addAll(callResponse.getResult().getItems());
packet.put(CONFIGMAPS, list);

return doContinueListOrNext(callResponse, packet);
}

static List<V1ConfigMap> getConfigMaps(Packet packet) {
return Optional.ofNullable(getConfigMapsIfContinue(packet)).orElse(new ArrayList<>());
}

@SuppressWarnings("unchecked")
private static List<V1ConfigMap> getConfigMapsIfContinue(Packet packet) {
return packet.get(AsyncRequestStep.CONTINUE) != null ? (List<V1ConfigMap>) packet.get(CONFIGMAPS) : null;
}
}

static class DomainValidationStep extends Step {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
package oracle.kubernetes.operator.helpers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.LogRecord;
import java.util.stream.IntStream;

import com.meterware.simplestub.Memento;
import com.meterware.simplestub.StaticStubSupport;
import com.meterware.simplestub.Stub;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1Event;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Secret;
Expand All @@ -28,10 +31,12 @@
import oracle.kubernetes.operator.work.TerminalStep;
import oracle.kubernetes.utils.TestUtils;
import oracle.kubernetes.weblogic.domain.model.Cluster;
import oracle.kubernetes.weblogic.domain.model.Configuration;
import oracle.kubernetes.weblogic.domain.model.ConfigurationConstants;
import oracle.kubernetes.weblogic.domain.model.Domain;
import oracle.kubernetes.weblogic.domain.model.DomainStatus;
import oracle.kubernetes.weblogic.domain.model.ManagedServer;
import oracle.kubernetes.weblogic.domain.model.Model;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -42,6 +47,7 @@
import static oracle.kubernetes.operator.EventTestUtils.containsEventWithMessage;
import static oracle.kubernetes.operator.ProcessingConstants.DOMAIN_TOPOLOGY;
import static oracle.kubernetes.operator.ProcessingConstants.MAKE_RIGHT_DOMAIN_OPERATION;
import static oracle.kubernetes.operator.TuningParametersImpl.DEFAULT_CALL_LIMIT;
import static oracle.kubernetes.operator.helpers.KubernetesTestSupport.DOMAIN;
import static oracle.kubernetes.operator.helpers.ServiceHelperTestBase.NS;
import static oracle.kubernetes.operator.logging.MessageKeys.DOMAIN_VALIDATION_FAILED;
Expand All @@ -57,6 +63,17 @@
import static org.hamcrest.junit.MatcherAssert.assertThat;

public class DomainValidationStepTest {
/** More than one chunk's worth of secrets or configmaps. */
private static final int MULTI_CHUNKS_FIRST_NUM_IN_SECOND_CHUNK = DEFAULT_CALL_LIMIT + 1;
private static final int MULTI_CHUNKS_MIDDLE_NUM_IN_FIRST_CHUNK = DEFAULT_CALL_LIMIT / 2;
private static final int MULTI_CHUNKS_LAST_NUM = DEFAULT_CALL_LIMIT * 2 + 1;

private static final String SECRETS = "secrets";
private static final String CONFIGMAPS = "configmaps";

private static final String TEST_SECRET_PREFIX = "TEST_SECRET";
private static final String TEST_CONFIGMAP_PREFIX = "TEST_CM";

private final Domain domain = DomainProcessorTestSetup.createTestDomain();
private final DomainPresenceInfo info = new DomainPresenceInfo(domain);
private final TerminalStep terminalStep = new TerminalStep();
Expand Down Expand Up @@ -180,6 +197,146 @@ public void whenDomainRefersToDefinedSecret_runNextStep() {
assertThat(terminalStep.wasRun(), is(true));
}

@Test
public void whenDomainValidationStepsCalled_withSecretInMultiChunks_packetContainsAllSecrets() {
createSecrets(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(getMatchingSecretsCount(), is(MULTI_CHUNKS_LAST_NUM));
}

private int getMatchingSecretsCount() {
List<V1Secret> list =
(List<V1Secret>) Optional.ofNullable(testSupport.getPacket().get(SECRETS)).orElse(Collections.EMPTY_LIST);
return Math.toIntExact(list.stream().filter(secret -> matchesExpectedSecretNamePattern(secret)).count());
}

private boolean matchesExpectedSecretNamePattern(V1Secret secret) {
return Optional.of(secret).map(V1Secret::getMetadata).map(V1ObjectMeta::getName).orElse("")
.startsWith(TEST_SECRET_PREFIX);
}

@Test
public void whenDomainRefersToDefinedSecretInMiddleChunk_runNextStep() {
domain.getSpec().withWebLogicCredentialsSecret(
new V1SecretReference().name(TEST_SECRET_PREFIX + MULTI_CHUNKS_FIRST_NUM_IN_SECOND_CHUNK).namespace(NS));
createSecrets(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(terminalStep.wasRun(), is(true));
}

@Test
public void whenDomainRefersToDefinedSecretInFirstChunk_runNextStep() {
domain.getSpec().withWebLogicCredentialsSecret(
new V1SecretReference().name(TEST_SECRET_PREFIX + MULTI_CHUNKS_MIDDLE_NUM_IN_FIRST_CHUNK).namespace(NS));
createSecrets(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(terminalStep.wasRun(), is(true));
}

@Test
public void whenDomainRefersToDefinedSecretInLastChunk_runNextStep() {
domain.getSpec().withWebLogicCredentialsSecret(
new V1SecretReference().name(TEST_SECRET_PREFIX + MULTI_CHUNKS_LAST_NUM).namespace(NS));
createSecrets(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(terminalStep.wasRun(), is(true));
}

private void createSecrets(int lastSecretNum) {
IntStream.rangeClosed(1, lastSecretNum)
.boxed()
.map(i -> TEST_SECRET_PREFIX + i)
.map(this::createSecret)
.forEach(testSupport::defineResources);
}

private V1Secret createSecret(String secret) {
return new V1Secret().metadata(new V1ObjectMeta().name(secret).namespace(NS));
}

@Test
public void whenDomainValidationStepsCalled_withConfigMapInMultiChunks_packetContainsAllConfigMaps() {
createConfigMaps(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(getMatchingConfigMapsCount(), is(MULTI_CHUNKS_LAST_NUM));
}

private int getMatchingConfigMapsCount() {
List<V1ConfigMap> list =
(List<V1ConfigMap>) Optional.ofNullable(testSupport.getPacket().get(CONFIGMAPS)).orElse(Collections.EMPTY_LIST);
return Math.toIntExact(list.stream().filter(cm -> matchesExpectedConfigMapNamePattern(cm)).count());
}

private boolean matchesExpectedConfigMapNamePattern(V1ConfigMap cm) {
return Optional.of(cm).map(V1ConfigMap::getMetadata).map(V1ObjectMeta::getName).orElse("")
.startsWith(TEST_CONFIGMAP_PREFIX);
}

@Test
public void whenDomainRefersToDefinedConfigMapInMiddleChunk_runNextStep() {
domain.getSpec()
.withWebLogicCredentialsSecret(new V1SecretReference().name("name"))
.setConfiguration(new Configuration().withModel(
new Model().withConfigMap(TEST_CONFIGMAP_PREFIX + MULTI_CHUNKS_FIRST_NUM_IN_SECOND_CHUNK)
.withRuntimeEncryptionSecret("name")));

testSupport.defineResources(new V1Secret().metadata(new V1ObjectMeta().name("name").namespace(NS)));

createConfigMaps(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(terminalStep.wasRun(), is(true));
}

@Test
public void whenDomainRefersToDefinedConfigMapInFirstChunk_runNextStep() {
domain.getSpec()
.withWebLogicCredentialsSecret(new V1SecretReference().name("name"))
.setConfiguration(new Configuration().withModel(
new Model().withConfigMap(TEST_CONFIGMAP_PREFIX + MULTI_CHUNKS_MIDDLE_NUM_IN_FIRST_CHUNK)
.withRuntimeEncryptionSecret("name")));

testSupport.defineResources(new V1Secret().metadata(new V1ObjectMeta().name("name").namespace(NS)));

createConfigMaps(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(terminalStep.wasRun(), is(true));
}

@Test
public void whenDomainRefersToDefinedConfigMapInLastChunk_runNextStep() {
domain.getSpec()
.withWebLogicCredentialsSecret(new V1SecretReference().name("name"))
.setConfiguration(new Configuration().withModel(
new Model().withConfigMap(TEST_CONFIGMAP_PREFIX + MULTI_CHUNKS_LAST_NUM)
.withRuntimeEncryptionSecret("name")));

testSupport.defineResources(new V1Secret().metadata(new V1ObjectMeta().name("name").namespace(NS)));

createConfigMaps(MULTI_CHUNKS_LAST_NUM);
testSupport.runSteps(domainValidationSteps);

assertThat(terminalStep.wasRun(), is(true));
}

private void createConfigMaps(int lastConfigMapNum) {
IntStream.rangeClosed(1, lastConfigMapNum)
.boxed()
.map(i -> TEST_CONFIGMAP_PREFIX + i)
.map(this::createConfigMap)
.forEach(testSupport::defineResources);
}

private V1ConfigMap createConfigMap(String cm) {
return new V1ConfigMap().metadata(new V1ObjectMeta().name(cm).namespace(NS));
}

@Test
public void whenClusterDoesNotExistInDomain_logWarning() {
domain.getSpec().withCluster(createCluster("no-such-cluster"));
Expand Down Expand Up @@ -241,7 +398,7 @@ public void whenBothServerAndClusterDoNotExistInDomain_createEventWithBothWarnin
}

@Test
public void whenIsExplictRecheck_doNotCreateEvent() {
public void whenIsExplicitRecheck_doNotCreateEvent() {
consoleControl.ignoreMessage(NO_CLUSTER_IN_DOMAIN);
setExplicitRecheck();
domain.getSpec().withCluster(createCluster("no-such-cluster"));
Expand Down