Skip to content

Commit a5c2c30

Browse files
authored
Cache secret for a configurable time period and reset on auth/authz failure (#2326)
* Clear cached secret on auth or authz failure * Add tunable and have tests use SystemClock delegate
1 parent 6f38c6b commit a5c2c30

30 files changed

+238
-99
lines changed

operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import oracle.kubernetes.operator.watcher.WatchListener;
3535
import oracle.kubernetes.operator.work.Packet;
3636
import oracle.kubernetes.operator.work.Step;
37+
import oracle.kubernetes.utils.SystemClock;
3738

3839
/** Watches for Jobs to become Ready or leave Ready state. */
3940
public class JobWatcher extends Watcher<V1Job> implements WatchListener<V1Job>, JobAwaiterStepFactory {
@@ -322,7 +323,7 @@ public String toString() {
322323

323324
private long getJobStartedSeconds() {
324325
if (job.getStatus() != null && job.getStatus().getStartTime() != null) {
325-
return ChronoUnit.SECONDS.between(job.getStatus().getStartTime(), OffsetDateTime.now());
326+
return ChronoUnit.SECONDS.between(job.getStatus().getStartTime(), SystemClock.now());
326327
}
327328
return -1;
328329
}

operator/src/main/java/oracle/kubernetes/operator/Main.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import oracle.kubernetes.operator.work.Packet;
5858
import oracle.kubernetes.operator.work.Step;
5959
import oracle.kubernetes.operator.work.ThreadFactorySingleton;
60+
import oracle.kubernetes.utils.SystemClock;
6061
import oracle.kubernetes.weblogic.domain.model.DomainList;
6162

6263
import static oracle.kubernetes.operator.helpers.NamespaceHelper.getOperatorNamespace;
@@ -74,7 +75,7 @@ public class Main {
7475
private static final ScheduledExecutorService wrappedExecutorService =
7576
Engine.wrappedExecutorService("operator", container);
7677
private static final AtomicReference<OffsetDateTime> lastFullRecheck =
77-
new AtomicReference<>(OffsetDateTime.now());
78+
new AtomicReference<>(SystemClock.now());
7879
private static final Semaphore shutdownSignal = new Semaphore(0);
7980
private static final int DEFAULT_STUCK_POD_RECHECK_SECONDS = 30;
8081

operator/src/main/java/oracle/kubernetes/operator/ServerStatusReader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import java.io.IOException;
77
import java.io.InputStreamReader;
88
import java.io.Reader;
9-
import java.time.OffsetDateTime;
109
import java.util.Collection;
1110
import java.util.Optional;
1211
import java.util.concurrent.ConcurrentHashMap;
@@ -38,6 +37,7 @@
3837
import oracle.kubernetes.operator.work.Packet;
3938
import oracle.kubernetes.operator.work.Step;
4039
import oracle.kubernetes.utils.OperatorUtils;
40+
import oracle.kubernetes.utils.SystemClock;
4141
import oracle.kubernetes.weblogic.domain.model.ServerHealth;
4242

4343
import static oracle.kubernetes.operator.KubernetesConstants.CONTAINER_NAME;
@@ -149,7 +149,7 @@ public NextAction apply(Packet packet) {
149149
if (lastKnownStatus != null
150150
&& !WebLogicConstants.UNKNOWN_STATE.equals(lastKnownStatus.getStatus())
151151
&& lastKnownStatus.getUnchangedCount() >= main.unchangedCountToDelayStatusRecheck) {
152-
if (OffsetDateTime.now()
152+
if (SystemClock.now()
153153
.isBefore(lastKnownStatus.getTime().plusSeconds((int) main.eventualLongDelay))) {
154154
String state = lastKnownStatus.getStatus();
155155
serverStateMap.put(serverName, state);

operator/src/main/java/oracle/kubernetes/operator/TuningParameters.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class MainTuning {
4040
public final int stuckPodRecheckSeconds;
4141
public final long initialShortDelay;
4242
public final long eventualLongDelay;
43+
public final int weblogicCredentialsSecretRereadIntervalSeconds;
4344

4445
/**
4546
* create main tuning.
@@ -53,6 +54,7 @@ class MainTuning {
5354
* @param stuckPodRecheckSeconds time between checks for stuck pods
5455
* @param initialShortDelay initial short delay
5556
* @param eventualLongDelay eventual long delay
57+
* @param weblogicCredentialsSecretRereadIntervalSeconds credentials secret reread interval
5658
*/
5759
public MainTuning(
5860
int initializationRetryDelaySeconds,
@@ -64,7 +66,8 @@ public MainTuning(
6466
int unchangedCountToDelayStatusRecheck,
6567
int stuckPodRecheckSeconds,
6668
long initialShortDelay,
67-
long eventualLongDelay) {
69+
long eventualLongDelay,
70+
int weblogicCredentialsSecretRereadIntervalSeconds) {
6871
this.initializationRetryDelaySeconds = initializationRetryDelaySeconds;
6972
this.domainPresenceFailureRetrySeconds = domainPresenceFailureRetrySeconds;
7073
this.domainPresenceFailureRetryMaxCount = domainPresenceFailureRetryMaxCount;
@@ -75,6 +78,7 @@ public MainTuning(
7578
this.stuckPodRecheckSeconds = stuckPodRecheckSeconds;
7679
this.initialShortDelay = initialShortDelay;
7780
this.eventualLongDelay = eventualLongDelay;
81+
this.weblogicCredentialsSecretRereadIntervalSeconds = weblogicCredentialsSecretRereadIntervalSeconds;
7882
}
7983

8084
@Override
@@ -88,6 +92,7 @@ public String toString() {
8892
.append("unchangedCountToDelayStatusRecheck", unchangedCountToDelayStatusRecheck)
8993
.append("initialShortDelay", initialShortDelay)
9094
.append("eventualLongDelay", eventualLongDelay)
95+
.append("weblogicCredentialsSecretRereadIntervalSeconds", weblogicCredentialsSecretRereadIntervalSeconds)
9196
.toString();
9297
}
9398

@@ -102,6 +107,7 @@ public int hashCode() {
102107
.append(unchangedCountToDelayStatusRecheck)
103108
.append(initialShortDelay)
104109
.append(eventualLongDelay)
110+
.append(weblogicCredentialsSecretRereadIntervalSeconds)
105111
.toHashCode();
106112
}
107113

@@ -123,6 +129,7 @@ public boolean equals(Object o) {
123129
.append(unchangedCountToDelayStatusRecheck, mt.unchangedCountToDelayStatusRecheck)
124130
.append(initialShortDelay, mt.initialShortDelay)
125131
.append(eventualLongDelay, mt.eventualLongDelay)
132+
.append(weblogicCredentialsSecretRereadIntervalSeconds, mt.weblogicCredentialsSecretRereadIntervalSeconds)
126133
.isEquals();
127134
}
128135
}

operator/src/main/java/oracle/kubernetes/operator/TuningParametersImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ private void update() {
5757
(int) readTuningParameter("statusUpdateUnchangedCountToDelayStatusRecheck", 10),
5858
(int) readTuningParameter("stuckPodRecheckSeconds", 30),
5959
readTuningParameter("statusUpdateInitialShortDelay", 5),
60-
readTuningParameter("statusUpdateEventualLongDelay", 30));
60+
readTuningParameter("statusUpdateEventualLongDelay", 30),
61+
(int) readTuningParameter("weblogicCredentialsSecretRereadIntervalSeconds", 120));
6162

6263
CallBuilderTuning callBuilder =
6364
new CallBuilderTuning(

operator/src/main/java/oracle/kubernetes/operator/helpers/AuthorizationHeaderFactory.java renamed to operator/src/main/java/oracle/kubernetes/operator/helpers/AuthorizationSource.java

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,26 @@
44
package oracle.kubernetes.operator.helpers;
55

66
import java.util.Base64;
7-
import javax.annotation.Nonnull;
87

9-
public class AuthorizationHeaderFactory {
10-
private final byte[] encodedUsername;
11-
private final byte[] encodedPassword;
8+
public interface AuthorizationSource {
129

13-
public AuthorizationHeaderFactory(@Nonnull byte[] userName, @Nonnull byte[] password) {
14-
encodedUsername = encode(userName);
15-
encodedPassword = encode(password);
16-
}
10+
byte[] getUserName();
1711

18-
private byte[] encode(byte[] source) {
19-
return Base64.getEncoder().encode(source);
20-
}
12+
byte[] getPassword();
2113

22-
public String createBasicAuthorizationString() {
23-
return "Basic " + createEncodedBasicCredentials(decode(encodedUsername), decode(encodedPassword));
14+
/**
15+
* Create an HTTP basic authorization header using the credentials.
16+
* @return Basic authorization header
17+
*/
18+
default String createBasicAuthorizationString() {
19+
return "Basic " + createEncodedBasicCredentials(getUserName(), getPassword());
2420
}
2521

26-
private byte[] decode(byte[] source) {
27-
return Base64.getDecoder().decode(source);
22+
/**
23+
* Notification that the credentials, when used, resulted in an authentication or authorization failure.
24+
*/
25+
default void onFailure() {
26+
// no-op
2827
}
2928

3029
// Create encoded credentials from username and password.
@@ -35,10 +34,4 @@ private static String createEncodedBasicCredentials(final byte[] username, final
3534
System.arraycopy(password, 0, usernameAndPassword, username.length + 1, password.length);
3635
return Base64.getEncoder().encodeToString(usernameAndPassword);
3736
}
38-
39-
public static class SecretDataMissingException extends RuntimeException {
40-
41-
SecretDataMissingException() {
42-
}
43-
}
4437
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import java.io.BufferedReader;
77
import java.io.IOException;
88
import java.io.StringReader;
9-
import java.time.OffsetDateTime;
109
import java.util.ArrayList;
1110
import java.util.Collection;
1211
import java.util.Collections;
@@ -44,6 +43,7 @@
4443
import oracle.kubernetes.operator.work.NextAction;
4544
import oracle.kubernetes.operator.work.Packet;
4645
import oracle.kubernetes.operator.work.Step;
46+
import oracle.kubernetes.utils.SystemClock;
4747
import oracle.kubernetes.weblogic.domain.model.Domain;
4848
import org.apache.commons.lang3.builder.HashCodeBuilder;
4949
import org.yaml.snakeyaml.Yaml;
@@ -545,7 +545,7 @@ boolean isTopologyNotValid() {
545545

546546
private void updatePacket() {
547547
ScanCache.INSTANCE.registerScan(
548-
info.getNamespace(), info.getDomainUid(), new Scan(wlsDomainConfig, OffsetDateTime.now()));
548+
info.getNamespace(), info.getDomainUid(), new Scan(wlsDomainConfig, SystemClock.now()));
549549
packet.put(ProcessingConstants.DOMAIN_TOPOLOGY, wlsDomainConfig);
550550

551551
copyFileToPacketIfPresent(DOMAINZIP_HASH, DOMAINZIP_HASH);
@@ -861,7 +861,7 @@ private void recordTopology(Packet packet, DomainPresenceInfo info, DomainTopolo
861861
ScanCache.INSTANCE.registerScan(
862862
info.getNamespace(),
863863
info.getDomainUid(),
864-
new Scan(domainTopology.getDomain(), OffsetDateTime.now()));
864+
new Scan(domainTopology.getDomain(), SystemClock.now()));
865865

866866
packet.put(ProcessingConstants.DOMAIN_TOPOLOGY, domainTopology.getDomain());
867867
}

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package oracle.kubernetes.operator.helpers;
55

6+
import java.time.OffsetDateTime;
67
import java.util.ArrayList;
78
import java.util.Collection;
89
import java.util.Collections;
@@ -14,6 +15,8 @@
1415
import java.util.concurrent.atomic.AtomicBoolean;
1516
import java.util.concurrent.atomic.AtomicInteger;
1617
import java.util.concurrent.atomic.AtomicReference;
18+
import java.util.concurrent.locks.ReadWriteLock;
19+
import java.util.concurrent.locks.ReentrantReadWriteLock;
1720
import java.util.function.Predicate;
1821
import java.util.stream.Stream;
1922
import javax.annotation.Nonnull;
@@ -22,11 +25,14 @@
2225
import io.kubernetes.client.openapi.models.V1EnvVar;
2326
import io.kubernetes.client.openapi.models.V1ObjectMeta;
2427
import io.kubernetes.client.openapi.models.V1Pod;
28+
import io.kubernetes.client.openapi.models.V1Secret;
2529
import io.kubernetes.client.openapi.models.V1Service;
2630
import io.kubernetes.client.openapi.models.V1beta1PodDisruptionBudget;
31+
import oracle.kubernetes.operator.TuningParameters;
2732
import oracle.kubernetes.operator.WebLogicConstants;
2833
import oracle.kubernetes.operator.wlsconfig.WlsServerConfig;
2934
import oracle.kubernetes.operator.work.Packet;
35+
import oracle.kubernetes.utils.SystemClock;
3036
import oracle.kubernetes.weblogic.domain.model.Domain;
3137
import oracle.kubernetes.weblogic.domain.model.ServerSpec;
3238
import org.apache.commons.lang3.builder.EqualsBuilder;
@@ -55,6 +61,9 @@ public class DomainPresenceInfo {
5561
private final ConcurrentMap<String, ServerKubernetesObjects> servers = new ConcurrentHashMap<>();
5662
private final ConcurrentMap<String, V1Service> clusters = new ConcurrentHashMap<>();
5763
private final ConcurrentMap<String, V1beta1PodDisruptionBudget> podDisruptionBudgets = new ConcurrentHashMap<>();
64+
private final ReadWriteLock webLogicCredentialsSecretLock = new ReentrantReadWriteLock();
65+
private V1Secret webLogicCredentialsSecret;
66+
private OffsetDateTime webLogicCredentialsSecretLastSet;
5867

5968
private final List<String> validationWarnings = Collections.synchronizedList(new ArrayList<>());
6069
private EventItem lastEventItem;
@@ -454,6 +463,43 @@ boolean deleteClusterServiceFromEvent(String clusterName, V1Service event) {
454463
s -> !KubernetesUtils.isFirstNewer(getMetadata(s), getMetadata(event)));
455464
}
456465

466+
/**
467+
* Retrieve the WebLogic credentials secret, if cached. This cached value will be automatically cleared
468+
* after a configured time period.
469+
* @return Cached secret value
470+
*/
471+
public V1Secret getWebLogicCredentialsSecret() {
472+
webLogicCredentialsSecretLock.readLock().lock();
473+
try {
474+
if (webLogicCredentialsSecretLastSet == null
475+
|| webLogicCredentialsSecretLastSet.isAfter(
476+
SystemClock.now().minusSeconds(
477+
TuningParameters.getInstance().getMainTuning().weblogicCredentialsSecretRereadIntervalSeconds))) {
478+
return webLogicCredentialsSecret;
479+
}
480+
} finally {
481+
webLogicCredentialsSecretLock.readLock().unlock();
482+
}
483+
484+
// time to clear
485+
setWebLogicCredentialsSecret(null);
486+
return null;
487+
}
488+
489+
/**
490+
* Cache the WebLogic credentials secret.
491+
* @param webLogicCredentialsSecret Secret value
492+
*/
493+
public void setWebLogicCredentialsSecret(V1Secret webLogicCredentialsSecret) {
494+
webLogicCredentialsSecretLock.writeLock().lock();
495+
try {
496+
webLogicCredentialsSecretLastSet = (webLogicCredentialsSecret != null) ? SystemClock.now() : null;
497+
this.webLogicCredentialsSecret = webLogicCredentialsSecret;
498+
} finally {
499+
webLogicCredentialsSecretLock.writeLock().unlock();
500+
}
501+
}
502+
457503
private V1Service getNewerService(V1Service first, V1Service second) {
458504
return KubernetesUtils.isFirstNewer(getMetadata(first), getMetadata(second)) ? first : second;
459505
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import oracle.kubernetes.operator.work.NextAction;
2525
import oracle.kubernetes.operator.work.Packet;
2626
import oracle.kubernetes.operator.work.Step;
27+
import oracle.kubernetes.utils.SystemClock;
2728
import oracle.kubernetes.weblogic.domain.model.Domain;
2829

2930
import static oracle.kubernetes.operator.DomainProcessorImpl.getEventK8SObjects;
@@ -647,7 +648,7 @@ public String getMessage(EventData eventData) {
647648
}
648649

649650
OffsetDateTime getCurrentTimestamp() {
650-
return OffsetDateTime.now();
651+
return SystemClock.now();
651652
}
652653

653654
void addLabels(V1ObjectMeta metadata, EventData eventData) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.time.OffsetDateTime;
77

8+
import oracle.kubernetes.utils.SystemClock;
89
import org.apache.commons.lang3.builder.EqualsBuilder;
910
import org.apache.commons.lang3.builder.HashCodeBuilder;
1011
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -26,7 +27,7 @@ public LastKnownStatus(String status) {
2627
public LastKnownStatus(String status, int unchangedCount) {
2728
this.status = status;
2829
this.unchangedCount = unchangedCount;
29-
this.time = OffsetDateTime.now();
30+
this.time = SystemClock.now();
3031
}
3132

3233
public String getStatus() {

0 commit comments

Comments
 (0)