Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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.json;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;

/** Specifies that this field is related to the named feature. */
@Retention(RetentionPolicy.RUNTIME)
@Target(FIELD)
public @interface Feature {

/**
* Feature name.
*
* @return feature name
*/
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class SchemaGenerator {
private final Collection<String> suppressDescriptionForPackages = new ArrayList<>();
private final Map<Class<?>, String> additionalPropertiesTypes = new HashMap<>();

private final Collection<String> enabledFeatures = new ArrayList<>();

/**
* Returns a pretty-printed string corresponding to a generated schema.
*
Expand Down Expand Up @@ -195,7 +197,7 @@ void generateFieldIn(Map<String, Object> map, Field field) {
}

private boolean includeInSchema(Field field) {
return !isStatic(field) && !isVolatile(field);
return !isStatic(field) && !isVolatile(field) && !isDisabledFeature(field);
}

private boolean isStatic(Field field) {
Expand All @@ -210,6 +212,11 @@ private boolean isDeprecated(Field field) {
return field.getAnnotation(Deprecated.class) != null;
}

private boolean isDisabledFeature(Field field) {
Feature feature = field.getAnnotation(Feature.class);
return feature != null && !enabledFeatures.contains(feature.value());
}

private String getPropertyName(Field field) {
SerializedName serializedName = field.getAnnotation(SerializedName.class);
if (serializedName != null && serializedName.value().length() > 0) {
Expand Down Expand Up @@ -502,6 +509,10 @@ public void defineAdditionalProperties(Class<?> forClass, String additionalPrope
additionalPropertiesTypes.put(forClass, additionalPropertyType);
}

public void defineEnabledFeatures(Collection<String> enabledFeatures) {
this.enabledFeatures.addAll(enabledFeatures);
}

private class SubSchemaGenerator {
final Field field;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -215,6 +216,22 @@ public void doNotGenerateSchemaForStatics() {
assertThat(schema, hasJsonPath("$.required", not(arrayContaining("staticInt"))));
}

@Test
public void generateSchemaForEnabledFeature() throws NoSuchFieldException {
generator.defineEnabledFeatures(List.of("Binding"));
Object schema = generator.generate(SimpleObject.class);

assertThat(schema, hasJsonPath("$.properties.fieldAssociatedWithBindingFeature"));
}

@Test
public void doNotGenerateSchemaForDisabledFeature() {
Object schema = generator.generate(SimpleObject.class);

assertThat(schema, hasNoJsonPath("$.properties.fieldAssociatedWithMountFeature"));
assertThat(schema, hasJsonPath("$.required", not(arrayContaining("fieldAssociatedWithMountFeature"))));
}

@SuppressWarnings("unused")
@Test
public void generateSchemaForSimpleObject() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ class SimpleObject {
private float aaFloat;

private Map<String,Integer> keys;

@Feature("Mount")
private String fieldAssociatedWithMountFeature;

@Feature("Binding")
private String fieldAssociatedWithBindingFeature;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ data:
{{- if .dns1123Fields }}
dns1123Fields: {{ .dns1123Fields | quote }}
{{- end }}
{{- if .featureGates }}
featureGates: {{ .featureGates | quote }}
{{- end }}
{{- if .introspectorJobNameSuffix }}
introspectorJobNameSuffix: {{ .introspectorJobNameSuffix | quote }}
{{- end }}
Expand Down
7 changes: 7 additions & 0 deletions kubernetes/charts/weblogic-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ elasticSearchHost: "elasticsearch.default.svc.cluster.local"
# This parameter is ignored if 'elkIntegrationEnabled' is false.
elasticSearchPort: 9200

# featureGates specifies a set of key=value pairs separated by commas that describe whether a given
# operator feature is enabled. You enable a feature by including a key=value pair where the key is the
# feature name and the value is "true". This will allow the operator team to release features that
# are not yet ready to be enabled by default, but that are ready for testing by customers. Once a feature is
# stable then it will be enabled by default and can not be disabled using this configuration.
# featureGates: "...,CommonMounts=true"

# javaLoggingLevel specifies the Java logging level for the operator. This affects the operator pod's
# log output and the contents of log files in the container's /logs/ directory.
# Valid values are: "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", and "FINEST".
Expand Down
2 changes: 2 additions & 0 deletions operator/src/main/java/oracle/kubernetes/operator/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ private static String getBuildProperty(Properties buildProps, String key) {

private void logStartup(LoggingFacade loggingFacade) {
loggingFacade.info(MessageKeys.OPERATOR_STARTED, buildVersion, operatorImpl, operatorBuildTime);
Optional.ofNullable(TuningParameters.getInstance().getFeatureGates().getEnabledFeatures())
.ifPresent(ef -> loggingFacade.info(MessageKeys.ENABLED_FEATURES, ef));
loggingFacade.info(MessageKeys.OP_CONFIG_NAMESPACE, getOperatorNamespace());
loggingFacade.info(MessageKeys.OP_CONFIG_SERVICE_ACCOUNT, serviceAccountName);
Optional.ofNullable(Namespaces.getConfiguredDomainNamespaces())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

package oracle.kubernetes.operator;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;

Expand All @@ -29,6 +31,8 @@ static TuningParameters getInstance() {

PodTuning getPodTuning();

FeatureGates getFeatureGates();

class MainTuning {
public final int initializationRetryDelaySeconds;
public final int domainPresenceFailureRetrySeconds;
Expand Down Expand Up @@ -314,4 +318,48 @@ public boolean equals(Object o) {
.isEquals();
}
}

class FeatureGates {
public final Collection<String> enabledFeatures;

public FeatureGates(Collection<String> enabledFeatures) {
this.enabledFeatures = Collections.unmodifiableCollection(enabledFeatures);
}

public Collection<String> getEnabledFeatures() {
return enabledFeatures;
}

public boolean isFeatureEnabled(String featureName) {
return enabledFeatures.contains(featureName);
}

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

@Override
public int hashCode() {
return new HashCodeBuilder()
.append(enabledFeatures)
.toHashCode();
}

@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof FeatureGates)) {
return false;
}
FeatureGates fg = (FeatureGates) o;
return new EqualsBuilder()
.append(enabledFeatures, fg.enabledFeatures)
.isEquals();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@

package oracle.kubernetes.operator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

import oracle.kubernetes.operator.helpers.ConfigMapConsumer;
import oracle.kubernetes.operator.logging.LoggingFacade;
Expand All @@ -23,6 +27,7 @@ public class TuningParametersImpl extends ConfigMapConsumer implements TuningPar
private CallBuilderTuning callBuilder = null;
private WatchTuning watch = null;
private PodTuning pod = null;
private FeatureGates featureGates = null;

private TuningParametersImpl(ScheduledExecutorService executorService) {
super(executorService);
Expand Down Expand Up @@ -82,23 +87,40 @@ private void update() {
(int) readTuningParameter("livenessProbePeriodSeconds", 45),
readTuningParameter("introspectorJobActiveDeadlineSeconds", 120));

FeatureGates featureGates =
new FeatureGates(generateFeatureGates(get("featureGates")));

lock.writeLock().lock();
try {
if (!main.equals(this.main)
|| !callBuilder.equals(this.callBuilder)
|| !watch.equals(this.watch)
|| !pod.equals(this.pod)) {
|| !watch.equals(this.pod)
|| !pod.equals(this.featureGates)) {
LOGGER.info(MessageKeys.TUNING_PARAMETERS);
}
this.main = main;
this.callBuilder = callBuilder;
this.watch = watch;
this.pod = pod;
this.featureGates = featureGates;
} finally {
lock.writeLock().unlock();
}
}

private Collection<String> generateFeatureGates(String featureGatesProperty) {
Collection<String> enabledGates = new ArrayList<>();
if (featureGatesProperty != null) {
Arrays.stream(
featureGatesProperty.split(","))
.filter(s -> s.endsWith("=true"))
.map(s -> s.substring(s.indexOf('=')))
.collect(Collectors.toCollection(() -> enabledGates));
}
return enabledGates;
}

@Override
public MainTuning getMainTuning() {
lock.readLock().lock();
Expand Down Expand Up @@ -138,4 +160,14 @@ public PodTuning getPodTuning() {
lock.readLock().unlock();
}
}

@Override
public FeatureGates getFeatureGates() {
lock.readLock().lock();
try {
return featureGates;
} finally {
lock.readLock().unlock();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class MessageKeys {
public static final String OPERATOR_STARTED = "WLSKO-0000";
public static final String CREATING_API_CLIENT = "WLSKO-0001";
public static final String K8S_MASTER_URL = "WLSKO-0002";
public static final String ENABLED_FEATURES = "WLSKO-0003";
public static final String OPERATOR_SHUTTING_DOWN = "WLSKO-0005";
public static final String EXCEPTION = "WLSKO-0006";
public static final String CREATING_CRD = "WLSKO-0012";
Expand All @@ -19,11 +20,9 @@ public class MessageKeys {
public static final String SECRET_DATA_NOT_FOUND = "WLSKO-0020";
public static final String WLS_CONFIGURATION_READ = "WLSKO-0021";
public static final String JSON_PARSING_FAILED = "WLSKO-0026";
public static final String SERVICE_URL = "WLSKO-0027";
public static final String NO_WLS_SERVER_IN_CLUSTER = "WLSKO-0028";
public static final String VERIFY_ACCESS_START = "WLSKO-0029";
public static final String VERIFY_ACCESS_DENIED = "WLSKO-0030";
public static final String NAMESPACE_IS_DEFAULT = "WLSKO-0031";
public static final String STARTING_LIVENESS_THREAD = "WLSKO-0034";
public static final String COULD_NOT_CREATE_LIVENESS_FILE = "WLSKO-0035";
public static final String REST_AUTHENTICATION_MISSING_ACCESS_TOKEN = "WLSKO-0037";
Expand Down Expand Up @@ -138,7 +137,6 @@ public class MessageKeys {
public static final String CLUSTER_PDB_PATCHED = "WLSKO-0185";
public static final String BEGIN_MANAGING_NAMESPACE = "WLSKO-0186";
public static final String END_MANAGING_NAMESPACE = "WLSKO-0187";
public static final String MII_DOMAIN_DYNAMICALLY_UPDATED = "WLSKO-0188";
public static final String HTTP_REQUEST_GOT_THROWABLE = "WLSKO-0189";

// domain status messages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

package oracle.kubernetes.weblogic.domain.model;

import java.util.Collections;
import java.util.Optional;

import io.kubernetes.client.custom.Quantity;
import oracle.kubernetes.json.SchemaGenerator;
import oracle.kubernetes.operator.TuningParameters;

public class CrdSchemaGenerator {

Expand All @@ -18,6 +22,11 @@ public static SchemaGenerator createCrdSchemaGenerator() {
generator.setSupportObjectReferences(false);
generator.setIncludeSchemaReference(false);
generator.addPackageToSuppressDescriptions("io.kubernetes.client.openapi.models");
generator.defineEnabledFeatures(
Optional.ofNullable(TuningParameters.getInstance())
.map(TuningParameters::getFeatureGates)
.map(TuningParameters.FeatureGates::getEnabledFeatures)
.orElse(Collections.emptyList()));
return generator;
}
}
Loading