Skip to content

Commit 012699b

Browse files
ankediarussgold
andauthored
Changes to reduce webhook CPU usage. (#3870)
* Changes to reduce webhook CPU usage. Co-authored-by: Russell Gold <[email protected]>
1 parent 710cd17 commit 012699b

18 files changed

+494
-56
lines changed

integration-tests/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,11 @@
352352
<plugin>
353353
<groupId>org.jacoco</groupId>
354354
<artifactId>jacoco-maven-plugin</artifactId>
355+
<configuration>
356+
<excludes>
357+
<exclude>com/gargoylesoftware/**</exclude>
358+
</excludes>
359+
</configuration>
355360
</plugin>
356361
</plugins>
357362
</build>

operator/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@
287287
<plugin>
288288
<groupId>org.jacoco</groupId>
289289
<artifactId>jacoco-maven-plugin</artifactId>
290+
<configuration>
291+
<excludes>
292+
<exclude>com/gargoylesoftware/**</exclude>
293+
</excludes>
294+
</configuration>
290295
</plugin>
291296
</plugins>
292297
</build>

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package oracle.kubernetes.operator;
@@ -22,6 +22,14 @@ public interface CoreDelegate extends PacketComponent {
2222

2323
KubernetesVersion getKubernetesVersion();
2424

25+
String getDomainCrdResourceVersion();
26+
27+
void setDomainCrdResourceVersion(String resourceVersion);
28+
29+
String getClusterCrdResourceVersion();
30+
31+
void setClusterCrdResourceVersion(String resourceVersion);
32+
2533
File getDeploymentHome();
2634

2735
File getProbesHome();

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package oracle.kubernetes.operator;
@@ -35,6 +35,8 @@ public class CoreDelegateImpl implements CoreDelegate {
3535
protected final Engine engine;
3636
protected final String deploymentImpl;
3737
protected final String deploymentBuildTime;
38+
protected String domainCrdResourceVersion;
39+
protected String clusterCrdResourceVersion;
3840

3941
CoreDelegateImpl(Properties buildProps, ScheduledExecutorService scheduledExecutorService) {
4042
buildVersion = getBuildVersion(buildProps);
@@ -80,6 +82,26 @@ public KubernetesVersion getKubernetesVersion() {
8082
return kubernetesVersion;
8183
}
8284

85+
@Override
86+
public String getDomainCrdResourceVersion() {
87+
return domainCrdResourceVersion;
88+
}
89+
90+
@Override
91+
public void setDomainCrdResourceVersion(String resourceVersion) {
92+
this.domainCrdResourceVersion = resourceVersion;
93+
}
94+
95+
@Override
96+
public String getClusterCrdResourceVersion() {
97+
return clusterCrdResourceVersion;
98+
}
99+
100+
@Override
101+
public void setClusterCrdResourceVersion(String resourceVersion) {
102+
this.clusterCrdResourceVersion = resourceVersion;
103+
}
104+
83105
@Override
84106
public File getDeploymentHome() {
85107
return deploymentHome;

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

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
// Copyright (c) 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package oracle.kubernetes.operator;
55

6+
import java.util.Optional;
67
import java.util.Properties;
78
import java.util.concurrent.ScheduledExecutorService;
89
import java.util.concurrent.TimeUnit;
910
import java.util.concurrent.atomic.AtomicInteger;
1011

1112
import io.kubernetes.client.common.KubernetesListObject;
13+
import io.kubernetes.client.openapi.ApiException;
1214
import oracle.kubernetes.common.logging.MessageKeys;
1315
import oracle.kubernetes.operator.calls.CallResponse;
1416
import oracle.kubernetes.operator.helpers.CallBuilder;
@@ -29,6 +31,8 @@
2931
import static oracle.kubernetes.common.CommonConstants.SECRETS_WEBHOOK_CERT;
3032
import static oracle.kubernetes.common.CommonConstants.SECRETS_WEBHOOK_KEY;
3133
import static oracle.kubernetes.operator.EventConstants.OPERATOR_WEBHOOK_COMPONENT;
34+
import static oracle.kubernetes.operator.KubernetesConstants.CLUSTER_CRD_NAME;
35+
import static oracle.kubernetes.operator.KubernetesConstants.DOMAIN_CRD_NAME;
3236
import static oracle.kubernetes.operator.helpers.CrdHelper.createClusterCrdStep;
3337
import static oracle.kubernetes.operator.helpers.CrdHelper.createDomainCrdStep;
3438
import static oracle.kubernetes.operator.helpers.EventHelper.EventItem.WEBHOOK_STARTUP_FAILED;
@@ -47,7 +51,6 @@ public class WebhookMain extends BaseMain {
4751
private static NextStepFactory nextStepFactory = WebhookMain::createInitializeWebhookIdentityStep;
4852

4953
static class WebhookMainDelegateImpl extends CoreDelegateImpl implements WebhookMainDelegate {
50-
5154
public WebhookMainDelegateImpl(Properties buildProps, ScheduledExecutorService scheduledExecutorService) {
5255
super(buildProps, scheduledExecutorService);
5356
}
@@ -67,7 +70,6 @@ public String getWebhookCertUri() {
6770
public String getWebhookKeyUri() {
6871
return SECRETS_WEBHOOK_KEY;
6972
}
70-
7173
}
7274

7375
/**
@@ -153,13 +155,42 @@ Runnable recheckCrd() {
153155
}
154156

155157
Step createCRDRecheckSteps() {
158+
Step optimizedRecheckSteps = Step.chain(createDomainCRDPresenceCheck(), createClusterCRDPresenceCheck());
159+
try {
160+
String clusterCrdResourceVersion = getCrdResourceVersion(CLUSTER_CRD_NAME);
161+
String domainCrdResourceVersion = getCrdResourceVersion(DOMAIN_CRD_NAME);
162+
if (crdMissingOrChanged(clusterCrdResourceVersion, delegate.getClusterCrdResourceVersion())) {
163+
optimizedRecheckSteps = Step.chain(createClusterCrdStep(delegate.getProductVersion()), optimizedRecheckSteps);
164+
}
165+
if (crdMissingOrChanged(domainCrdResourceVersion, delegate.getDomainCrdResourceVersion())) {
166+
optimizedRecheckSteps = Step.chain(createDomainCrdStep(delegate.getProductVersion(),
167+
new Certificates(delegate)), optimizedRecheckSteps);
168+
}
169+
delegate.setDomainCrdResourceVersion(domainCrdResourceVersion);
170+
delegate.setClusterCrdResourceVersion(clusterCrdResourceVersion);
171+
return optimizedRecheckSteps;
172+
} catch (Exception e) {
173+
return createFullCRDRecheckSteps();
174+
}
175+
}
176+
177+
private String getCrdResourceVersion(String crdName) throws ApiException {
178+
return Optional.ofNullable(new CallBuilder().readCRDMetadata(crdName))
179+
.map(pom -> pom.getMetadata()).map(m -> m.getResourceVersion()).orElse(null);
180+
}
181+
182+
private Step createFullCRDRecheckSteps() {
156183
return Step.chain(
157184
createDomainCrdStep(delegate.getProductVersion(), new Certificates(delegate)),
158185
createClusterCrdStep(delegate.getProductVersion()),
159186
createDomainCRDPresenceCheck(),
160187
createClusterCRDPresenceCheck());
161188
}
162189

190+
private boolean crdMissingOrChanged(String crdResourceVersion, String cachedCrdResourceVersion) {
191+
return crdResourceVersion == null || !crdResourceVersion.equals(cachedCrdResourceVersion);
192+
}
193+
163194
// Returns a step that verifies the presence of an installed domain CRD. It does this by attempting to list the
164195
// domains in the operator's namespace. That should succeed (although usually returning an empty list)
165196
// if the CRD is present.

operator/src/main/java/oracle/kubernetes/operator/calls/RequestParams.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package oracle.kubernetes.operator.calls;
55

6+
import java.util.Arrays;
67
import javax.annotation.Nonnull;
78

89
import io.kubernetes.client.openapi.ApiException;
@@ -13,6 +14,7 @@
1314
import org.apache.commons.lang3.StringUtils;
1415

1516
public final class RequestParams {
17+
public static final String[] SUB_RESOURCE_SUFFIXES = {"Status", "Metadata"};
1618
private static final LoggingFacade LOGGER = LoggingFactory.getLogger("Operator", "Operator");
1719

1820
public final String call;
@@ -124,6 +126,15 @@ private int indexOfFirstCapitalInCallName() {
124126

125127
@Nonnull
126128
public String getOperationName() {
127-
return call.substring(0, indexOfFirstCapitalInCallName()) + (call.endsWith("Status") ? "Status" : "");
129+
return call.substring(0, indexOfFirstCapitalInCallName()) + getCallSuffix();
130+
}
131+
132+
@Nonnull
133+
private String getCallSuffix() {
134+
return Arrays.stream(SUB_RESOURCE_SUFFIXES).filter(this::isCallSuffix).findFirst().orElse("");
135+
}
136+
137+
private boolean isCallSuffix(String suffix) {
138+
return call.endsWith(suffix);
128139
}
129140
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package oracle.kubernetes.operator.helpers;
@@ -71,10 +71,12 @@
7171
import oracle.kubernetes.operator.tuning.TuningParameters;
7272
import oracle.kubernetes.operator.work.Step;
7373
import oracle.kubernetes.weblogic.domain.api.WeblogicApi;
74+
import oracle.kubernetes.weblogic.domain.api.WeblogicGenericApi;
7475
import oracle.kubernetes.weblogic.domain.model.ClusterList;
7576
import oracle.kubernetes.weblogic.domain.model.ClusterResource;
7677
import oracle.kubernetes.weblogic.domain.model.DomainList;
7778
import oracle.kubernetes.weblogic.domain.model.DomainResource;
79+
import oracle.kubernetes.weblogic.domain.model.PartialObjectMetadata;
7880

7981
import static oracle.kubernetes.operator.helpers.KubernetesUtils.getDomainUidLabel;
8082
import static oracle.kubernetes.utils.OperatorUtils.isNullOrEmpty;
@@ -479,6 +481,11 @@ public <T> T execute(
479481
RESOURCE_VERSION,
480482
timeoutSeconds,
481483
WATCH);
484+
485+
private final SynchronousCallFactory<PartialObjectMetadata> readCRDMetadataCall =
486+
(client, requestParams) ->
487+
new WeblogicGenericApi(client).readCustomResourceDefinitionMetadata(requestParams.name);
488+
482489
private final SynchronousCallFactory<DomainResource> readDomainCall =
483490
(client, requestParams) ->
484491
new WeblogicApi(client)
@@ -869,6 +876,18 @@ public Object replaceClusterUntyped(String name, String namespace, Map<String, O
869876
return executeSynchronousCall(requestParams, listDomainCall);
870877
}
871878

879+
/**
880+
* Get crd metadata.
881+
*
882+
* @return crd metadata
883+
* @throws ApiException API exception
884+
*/
885+
public @Nonnull PartialObjectMetadata readCRDMetadata(String name) throws ApiException {
886+
RequestParams requestParams = new RequestParams("readCRDMetadata", null,
887+
name, null, callParams);
888+
return executeSynchronousCall(requestParams, readCRDMetadataCall);
889+
}
890+
872891
private Call listDomainAsync(
873892
ApiClient client, String namespace, String cont, ApiCallback<DomainList> callback)
874893
throws ApiException {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package oracle.kubernetes.operator.helpers;
@@ -130,7 +130,8 @@ public void execute(Runnable command) {
130130
}
131131
};
132132
OkHttpClient httpClient =
133-
client.getHttpClient().newBuilder().dispatcher(new Dispatcher(exec)).build();
133+
client.getHttpClient().newBuilder().addInterceptor(new HeaderModifierInterceptor())
134+
.dispatcher(new Dispatcher(exec)).build();
134135
client.setHttpClient(httpClient);
135136
}
136137

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) 2023, Oracle 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.kubernetes.operator.helpers;
5+
6+
import java.util.Collections;
7+
import java.util.Optional;
8+
9+
import io.kubernetes.client.openapi.models.V1CustomResourceDefinition;
10+
import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionNames;
11+
import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionSpec;
12+
import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionVersion;
13+
import io.kubernetes.client.openapi.models.V1ObjectMeta;
14+
import oracle.kubernetes.operator.KubernetesConstants;
15+
import oracle.kubernetes.operator.LabelConstants;
16+
17+
public class CrdHelperTestBase {
18+
public static final SemanticVersion PRODUCT_VERSION = new SemanticVersion(3, 0, 0);
19+
20+
protected V1CustomResourceDefinition defineCrd(SemanticVersion operatorVersion, String crdName) {
21+
return new V1CustomResourceDefinition()
22+
.apiVersion("apiextensions.k8s.io/v1")
23+
.kind("CustomResourceDefinition")
24+
.metadata(createMetadata(operatorVersion, crdName))
25+
.spec(createSpec());
26+
}
27+
28+
@SuppressWarnings("SameParameterValue")
29+
private V1ObjectMeta createMetadata(SemanticVersion operatorVersion, String crdName) {
30+
return new V1ObjectMeta()
31+
.name(crdName)
32+
.putLabelsItem(LabelConstants.OPERATOR_VERSION,
33+
Optional.ofNullable(operatorVersion).map(SemanticVersion::toString).orElse(null));
34+
}
35+
36+
private V1CustomResourceDefinitionSpec createSpec() {
37+
return new V1CustomResourceDefinitionSpec()
38+
.group(KubernetesConstants.DOMAIN_GROUP)
39+
.scope("Namespaced")
40+
.addVersionsItem(new V1CustomResourceDefinitionVersion()
41+
.served(true).name(KubernetesConstants.OLD_DOMAIN_VERSION))
42+
.names(
43+
new V1CustomResourceDefinitionNames()
44+
.plural(KubernetesConstants.DOMAIN_PLURAL)
45+
.singular(KubernetesConstants.DOMAIN_SINGULAR)
46+
.kind(KubernetesConstants.DOMAIN)
47+
.shortNames(Collections.singletonList(KubernetesConstants.DOMAIN_SHORT)));
48+
}
49+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) 2023, Oracle 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.kubernetes.operator.helpers;
5+
6+
import java.io.IOException;
7+
import javax.annotation.Nonnull;
8+
9+
import okhttp3.Interceptor;
10+
import okhttp3.Request;
11+
import okhttp3.Response;
12+
13+
public class HeaderModifierInterceptor implements Interceptor {
14+
15+
private static final String PARTIAL_OBJECT_METADATA_HEADER =
16+
"application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1,application/json";
17+
18+
private static final ThreadLocal<Boolean> partialMetadataHeader = ThreadLocal.withInitial(() -> false);
19+
20+
/**
21+
* Intereceptor method to modify the request headers.
22+
*
23+
* @param chain Chain
24+
* @return Response response
25+
*/
26+
@Nonnull
27+
public Response intercept(Chain chain) throws IOException {
28+
final Request request = chain.request();
29+
final Request newRequest;
30+
if (Boolean.FALSE.equals(partialMetadataHeader.get())) {
31+
return chain.proceed(request);
32+
} else {
33+
newRequest = request.newBuilder()
34+
.header("Accept", PARTIAL_OBJECT_METADATA_HEADER)
35+
.build();
36+
return chain.proceed(newRequest);
37+
}
38+
}
39+
40+
public static void setPartialMetadataHeader(Boolean value) {
41+
partialMetadataHeader.set(value);
42+
}
43+
44+
public static void removePartialMetadataHeader() {
45+
partialMetadataHeader.remove();
46+
}
47+
}

0 commit comments

Comments
 (0)