Skip to content

Support clusterService.sessionAffinity #2383

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 4 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
48 changes: 28 additions & 20 deletions documentation/domains/Domain.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
},
"clusterService": {
"description": "Customization affecting Kubernetes Service generated for this WebLogic cluster.",
"$ref": "#/definitions/KubernetesResource"
"$ref": "#/definitions/ClusterService"
},
"maxConcurrentShutdown": {
"description": "The maximum number of WebLogic Server instances that will shut down in parallel for this cluster when it is being partially shut down by lowering its replica count. A value of 0 means there is no limit. Defaults to `spec.maxClusterConcurrentShutdown`, which defaults to 1.",
Expand Down Expand Up @@ -150,6 +150,33 @@
"clusterName"
]
},
"ClusterService": {
"type": "object",
"properties": {
"sessionAffinity": {
"description": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies",
"type": "string",
"enum": [
"ClientIP",
"None"
]
},
"annotations": {
"description": "The annotations to be added to generated resources.",
"additionalProperties": {
"type": "string"
},
"$ref": "#/definitions/Map"
},
"labels": {
"description": "The labels to be added to generated resources. The label names must not start with \"weblogic.\".",
"additionalProperties": {
"type": "string"
},
"$ref": "#/definitions/Map"
}
}
},
"ClusterStatus": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -493,25 +520,6 @@
}
}
},
"KubernetesResource": {
"type": "object",
"properties": {
"annotations": {
"description": "The annotations to be added to generated resources.",
"additionalProperties": {
"type": "string"
},
"$ref": "#/definitions/Map"
},
"labels": {
"description": "The labels to be added to generated resources. The label names must not start with \"weblogic.\".",
"additionalProperties": {
"type": "string"
},
"$ref": "#/definitions/Map"
}
}
},
"ManagedServer": {
"type": "object",
"properties": {
Expand Down
5 changes: 3 additions & 2 deletions documentation/domains/Domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall
| --- | --- | --- |
| `allowReplicasBelowMinDynClusterSize` | Boolean | Specifies whether the number of running cluster members is allowed to drop below the minimum dynamic cluster size configured in the WebLogic domain configuration. Otherwise, the operator will ensure that the number of running cluster members is not less than the minimum dynamic cluster setting. This setting applies to dynamic clusters only. Defaults to true. |
| `clusterName` | string | The name of the cluster. This value must match the name of a WebLogic cluster already defined in the WebLogic domain configuration. Required. |
| `clusterService` | [Kubernetes Resource](#kubernetes-resource) | Customization affecting Kubernetes Service generated for this WebLogic cluster. |
| `clusterService` | [Cluster Service](#cluster-service) | Customization affecting Kubernetes Service generated for this WebLogic cluster. |
| `maxConcurrentShutdown` | number | The maximum number of WebLogic Server instances that will shut down in parallel for this cluster when it is being partially shut down by lowering its replica count. A value of 0 means there is no limit. Defaults to `spec.maxClusterConcurrentShutdown`, which defaults to 1. |
| `maxConcurrentStartup` | number | The maximum number of Managed Servers instances that the operator will start in parallel for this cluster in response to a change in the `replicas` count. If more Managed Server instances must be started, the operator will wait until a Managed Server Pod is in the `Ready` state before starting the next Managed Server instance. A value of 0 means all Managed Server instances will start in parallel. Defaults to 0. |
| `maxUnavailable` | number | The maximum number of cluster members that can be temporarily unavailable. Defaults to 1. |
Expand Down Expand Up @@ -197,12 +197,13 @@ The current status of the operation of the WebLogic domain. Updated automaticall
| `channels` | array of [Channel](#channel) | Specifies which of the Administration Server's WebLogic channels should be exposed outside the Kubernetes cluster via a NodePort Service, along with the port for each channel. If not specified, the Administration Server's NodePort Service will not be created. |
| `labels` | Map | Labels to associate with the Administration Server's NodePort Service, if it is created. |

### Kubernetes Resource
### Cluster Service

| Name | Type | Description |
| --- | --- | --- |
| `annotations` | Map | The annotations to be added to generated resources. |
| `labels` | Map | The labels to be added to generated resources. The label names must not start with "weblogic.". |
| `sessionAffinity` | string | Supports "ClientIP" and "None". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies |

### Istio

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public class ClusterService {
@ApiModelProperty("The annotations to be attached to generated resources.")
private Map<String, String> annotations = new HashMap<>();

@ApiModelProperty(
"Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. "
+ "Must be ClientIP or None. Defaults to None. More info: "
+ "https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies")
private String sessionAffinity;

public ClusterService labels(Map<String, String> labels) {
this.labels = labels;
return this;
Expand Down Expand Up @@ -83,11 +89,20 @@ public void setAnnotations(Map<String, String> annotations) {
this.annotations = annotations;
}

public String getSessionAffinity() {
return sessionAffinity;
}

public void setSessionAffinity(String sessionAffinity) {
this.sessionAffinity = sessionAffinity;
}

@Override
public String toString() {
return new ToStringBuilder(this)
.append("labels", labels)
.append("annotations", annotations)
.append("sessionAffinity", sessionAffinity)
.toString();
}

Expand All @@ -104,11 +119,13 @@ public boolean equals(Object other) {
return new EqualsBuilder()
.append(labels, rhs.labels)
.append(annotations, rhs.annotations)
.append(sessionAffinity, rhs.sessionAffinity)
.isEquals();
}

@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(labels).append(annotations).toHashCode();
return new HashCodeBuilder(17, 37)
.append(labels).append(annotations).append(sessionAffinity).toHashCode();
}
}
11 changes: 10 additions & 1 deletion kubernetes/crd/domain-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
weblogic.sha256: 3e2981c2a3c6057a30a7d22c0a969b992f3a36051a133b6ba4e9fe20121d3088
weblogic.sha256: e091b25f525f2d1a6b90729dcee23d63ead5beb23660912acebf921d1c0499a7
name: domains.weblogic.oracle
spec:
group: weblogic.oracle
Expand Down Expand Up @@ -5590,6 +5590,15 @@ spec:
for this WebLogic cluster.
type: object
properties:
sessionAffinity:
description: 'Supports "ClientIP" and "None". Used to maintain
session affinity. Enable client IP based session affinity.
Must be ClientIP or None. Defaults to None. More info:
https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies'
type: string
enum:
- ClientIP
- None
annotations:
description: The annotations to be added to generated resources.
additionalProperties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,11 +401,13 @@ V1Service createRecipe() {
}

protected V1ServiceSpec createServiceSpec() {
return new V1ServiceSpec()
V1ServiceSpec spec = new V1ServiceSpec()
.type(getSpecType())
.putSelectorItem(LabelConstants.DOMAINUID_LABEL, getDomainUid())
.putSelectorItem(LabelConstants.CREATEDBYOPERATOR_LABEL, "true")
.ports(createServicePorts());
Optional.ofNullable(getSessionAffinity()).ifPresent(spec::setSessionAffinity);
return spec;
}

void addServicePorts(List<V1ServicePort> ports, WlsServerConfig serverConfig) {
Expand Down Expand Up @@ -527,6 +529,10 @@ boolean isIstioEnabled() {

abstract Map<String, String> getServiceAnnotations();

String getSessionAffinity() {
return null;
}

protected abstract void logServiceCreated(String messageKey);

protected abstract String getSpecType();
Expand Down Expand Up @@ -839,6 +845,11 @@ Map<String, String> getServiceLabels() {
Map<String, String> getServiceAnnotations() {
return getClusterSpec().getClusterAnnotations();
}

@Override
String getSessionAffinity() {
return getClusterSpec().getClusterSessionAffinity();
}
}

private static class ForExternalServiceStep extends ServiceHelperStep {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public class Cluster extends BaseConfiguration implements Comparable<Cluster> {
@Description("Customization affecting Kubernetes Service generated for this WebLogic cluster.")
@SerializedName("clusterService")
@Expose
private KubernetesResource clusterService = new KubernetesResource();
private ClusterService clusterService = new ClusterService();

@Description("Specifies whether the number of running cluster members is allowed to drop below the "
+ "minimum dynamic cluster size configured in the WebLogic domain configuration. "
Expand Down Expand Up @@ -112,7 +112,7 @@ public void setClusterName(@Nonnull String clusterName) {
this.clusterName = clusterName;
}

Cluster withClusterName(@Nonnull String clusterName) {
public Cluster withClusterName(@Nonnull String clusterName) {
setClusterName(clusterName);
return this;
}
Expand Down Expand Up @@ -167,14 +167,19 @@ public void setServerStartPolicy(String serverStartPolicy) {
this.serverStartPolicy = serverStartPolicy;
}

public KubernetesResource getClusterService() {
public ClusterService getClusterService() {
return clusterService;
}

public void setClusterService(KubernetesResource clusterService) {
public void setClusterService(ClusterService clusterService) {
this.clusterService = clusterService;
}

public Cluster withClusterService(ClusterService clusterService) {
this.setClusterService(clusterService);
return this;
}

public Map<String, String> getClusterLabels() {
return clusterService.getLabels();
}
Expand All @@ -191,6 +196,10 @@ void addClusterAnnotation(String name, String value) {
clusterService.addAnnotations(name, value);
}

public String getClusterSessionAffinity() {
return clusterService.getSessionAffinity();
}

Integer getMaxUnavailable() {
return maxUnavailable;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) 2021, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package oracle.kubernetes.weblogic.domain.model;

import java.util.Optional;

import oracle.kubernetes.json.Description;
import oracle.kubernetes.json.EnumClass;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

public class ClusterService extends KubernetesResource {

@EnumClass(SessionAffinity.class)
@Description(
"Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. "
+ "Must be ClientIP or None. Defaults to None. More info: "
+ "https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies")
private String sessionAffinity;

void fillInFrom(ClusterService clusterService1) {
super.fillInFrom(clusterService1);
this.sessionAffinity =
Optional.ofNullable(sessionAffinity).orElse(clusterService1.sessionAffinity);
}

public String getSessionAffinity() {
return sessionAffinity;
}

public void setSessionAffinity(String sessionAffinity) {
this.sessionAffinity = sessionAffinity;
}

public ClusterService withSessionAffinity(String sessionAffinity) {
this.sessionAffinity = sessionAffinity;
return this;
}

@Override
public String toString() {
return new ToStringBuilder(this)
.appendSuper(super.toString())
.append("sessionAffinity", sessionAffinity)
.toString();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (o == null || getClass() != o.getClass()) {
return false;
}

ClusterService that = (ClusterService) o;

return new EqualsBuilder()
.appendSuper(super.equals(o))
.append(sessionAffinity, that.sessionAffinity)
.isEquals();
}

@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.appendSuper(super.hashCode())
.append(sessionAffinity)
.toHashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public abstract class ClusterSpec {
@Nonnull
public abstract Map<String, String> getClusterAnnotations();

public abstract String getClusterSessionAffinity();

/**
* Returns the list of initContainers.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public Map<String, String> getClusterAnnotations() {
return cluster.getClusterAnnotations();
}

@Override
public String getClusterSessionAffinity() {
return cluster.getClusterSessionAffinity();
}

@Override
public List<V1Container> getInitContainers() {
return cluster.getInitContainers();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2021, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package oracle.kubernetes.weblogic.domain.model;

public enum SessionAffinity {
ClientIP,
None
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import oracle.kubernetes.operator.work.Step;
import oracle.kubernetes.weblogic.domain.DomainConfigurator;
import oracle.kubernetes.weblogic.domain.ServiceConfigurator;
import org.junit.jupiter.api.Test;

import static oracle.kubernetes.operator.logging.MessageKeys.CLUSTER_SERVICE_CREATED;
import static oracle.kubernetes.operator.logging.MessageKeys.CLUSTER_SERVICE_EXISTS;
import static oracle.kubernetes.operator.logging.MessageKeys.CLUSTER_SERVICE_REPLACED;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.junit.MatcherAssert.assertThat;

public class ClusterServiceHelperTest extends ServiceHelperTest {

Expand Down Expand Up @@ -105,4 +108,12 @@ String getExpectedSelectorValue() {
}
}

@Test
public void whenCreated_modelHasSessionAffinity() {
V1Service model = createService();

assertThat(
model.getSpec().getSessionAffinity(),
is("ClientIP"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
@SuppressWarnings("ConstantConditions")
abstract class ServiceHelperTest extends ServiceHelperTestBase {

private static final String TEST_CLUSTER = "cluster-1";
private static final int TEST_NODE_PORT = 30001;
private static final int TEST_NODE_SSL_PORT = 30002;
private static final int NAP1_NODE_PORT = 30012;
Expand Down
Loading