diff --git a/json-schema-generator/src/main/java/oracle/kubernetes/json/Feature.java b/json-schema-generator/src/main/java/oracle/kubernetes/json/Feature.java new file mode 100644 index 00000000000..9e8df426c57 --- /dev/null +++ b/json-schema-generator/src/main/java/oracle/kubernetes/json/Feature.java @@ -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(); +} diff --git a/json-schema-generator/src/main/java/oracle/kubernetes/json/SchemaGenerator.java b/json-schema-generator/src/main/java/oracle/kubernetes/json/SchemaGenerator.java index 045acd106cf..33411a9ea66 100644 --- a/json-schema-generator/src/main/java/oracle/kubernetes/json/SchemaGenerator.java +++ b/json-schema-generator/src/main/java/oracle/kubernetes/json/SchemaGenerator.java @@ -64,6 +64,8 @@ public class SchemaGenerator { private final Collection suppressDescriptionForPackages = new ArrayList<>(); private final Map, String> additionalPropertiesTypes = new HashMap<>(); + private final Collection enabledFeatures = new ArrayList<>(); + /** * Returns a pretty-printed string corresponding to a generated schema. * @@ -195,7 +197,7 @@ void generateFieldIn(Map 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) { @@ -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) { @@ -502,6 +509,10 @@ public void defineAdditionalProperties(Class forClass, String additionalPrope additionalPropertiesTypes.put(forClass, additionalPropertyType); } + public void defineEnabledFeatures(Collection enabledFeatures) { + this.enabledFeatures.addAll(enabledFeatures); + } + private class SubSchemaGenerator { final Field field; diff --git a/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java b/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java index 50395eae9e0..0371cb1344f 100644 --- a/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java +++ b/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java @@ -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; @@ -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() { diff --git a/json-schema-generator/src/test/java/oracle/kubernetes/json/SimpleObject.java b/json-schema-generator/src/test/java/oracle/kubernetes/json/SimpleObject.java index 315331ac940..ebc855c1610 100644 --- a/json-schema-generator/src/test/java/oracle/kubernetes/json/SimpleObject.java +++ b/json-schema-generator/src/test/java/oracle/kubernetes/json/SimpleObject.java @@ -23,4 +23,10 @@ class SimpleObject { private float aaFloat; private Map keys; + + @Feature("Mount") + private String fieldAssociatedWithMountFeature; + + @Feature("Binding") + private String fieldAssociatedWithBindingFeature; } diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-cm.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-cm.tpl index f66fc6808e5..dd6594de2b4 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-cm.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-cm.tpl @@ -34,6 +34,9 @@ data: {{- if .dns1123Fields }} dns1123Fields: {{ .dns1123Fields | quote }} {{- end }} + {{- if .featureGates }} + featureGates: {{ .featureGates | quote }} + {{- end }} {{- if .introspectorJobNameSuffix }} introspectorJobNameSuffix: {{ .introspectorJobNameSuffix | quote }} {{- end }} diff --git a/kubernetes/charts/weblogic-operator/values.yaml b/kubernetes/charts/weblogic-operator/values.yaml index e896d29b78f..3866190e387 100644 --- a/kubernetes/charts/weblogic-operator/values.yaml +++ b/kubernetes/charts/weblogic-operator/values.yaml @@ -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". diff --git a/operator/src/main/java/oracle/kubernetes/operator/Main.java b/operator/src/main/java/oracle/kubernetes/operator/Main.java index 46d071366ab..5282ad38bfb 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/Main.java +++ b/operator/src/main/java/oracle/kubernetes/operator/Main.java @@ -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()) diff --git a/operator/src/main/java/oracle/kubernetes/operator/TuningParameters.java b/operator/src/main/java/oracle/kubernetes/operator/TuningParameters.java index 2c19436cc75..041f59a1f35 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/TuningParameters.java +++ b/operator/src/main/java/oracle/kubernetes/operator/TuningParameters.java @@ -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; @@ -29,6 +31,8 @@ static TuningParameters getInstance() { PodTuning getPodTuning(); + FeatureGates getFeatureGates(); + class MainTuning { public final int initializationRetryDelaySeconds; public final int domainPresenceFailureRetrySeconds; @@ -314,4 +318,48 @@ public boolean equals(Object o) { .isEquals(); } } + + class FeatureGates { + public final Collection enabledFeatures; + + public FeatureGates(Collection enabledFeatures) { + this.enabledFeatures = Collections.unmodifiableCollection(enabledFeatures); + } + + public Collection 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(); + } + } } diff --git a/operator/src/main/java/oracle/kubernetes/operator/TuningParametersImpl.java b/operator/src/main/java/oracle/kubernetes/operator/TuningParametersImpl.java index 8e1861c607d..5b60dbf94e7 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/TuningParametersImpl.java +++ b/operator/src/main/java/oracle/kubernetes/operator/TuningParametersImpl.java @@ -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; @@ -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); @@ -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 generateFeatureGates(String featureGatesProperty) { + Collection 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(); @@ -138,4 +160,14 @@ public PodTuning getPodTuning() { lock.readLock().unlock(); } } + + @Override + public FeatureGates getFeatureGates() { + lock.readLock().lock(); + try { + return featureGates; + } finally { + lock.readLock().unlock(); + } + } } diff --git a/operator/src/main/java/oracle/kubernetes/operator/logging/MessageKeys.java b/operator/src/main/java/oracle/kubernetes/operator/logging/MessageKeys.java index 95b26cdd78e..aa97ecc119a 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/logging/MessageKeys.java +++ b/operator/src/main/java/oracle/kubernetes/operator/logging/MessageKeys.java @@ -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"; @@ -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"; @@ -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 diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGenerator.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGenerator.java index 6c77e64491a..5727253843d 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGenerator.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGenerator.java @@ -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 { @@ -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; } } diff --git a/operator/src/main/resources/Operator.properties b/operator/src/main/resources/Operator.properties index 07a83798927..9981d18317c 100644 --- a/operator/src/main/resources/Operator.properties +++ b/operator/src/main/resources/Operator.properties @@ -1,7 +1,8 @@ -WLSKO-0000=Oracle WebLogic Server Kubernetes Operator, version: {0}, implementation: {1}, build time: {2} +WLSKO-0000=Oracle WebLogic Kubernetes Operator, version: {0}, implementation: {1}, build time: {2} WLSKO-0001=Creating API Client -WLSKO-0002=The Kuberenetes Master URL is set to {0} -WLSKO-0005=The Oracle WebLogic Server Operator for Kubernetes is shutting down +WLSKO-0002=The Kubernetes Master URL is set to {0} +WLSKO-0003=The following optional operator features are enabled: {0} +WLSKO-0005=The Oracle WebLogic Kubernetes Operator is shutting down WLSKO-0006=Exception thrown WLSKO-0012=Create Custom Resource Definition: {0} WLSKO-0018={2} secret ''{0}'' not found in namespace ''{1}'' @@ -9,13 +10,11 @@ WLSKO-0019=Retrieving secret: {0} WLSKO-0020=Secret data field not found: {0} WLSKO-0021=Read server configurations from administration server took {0} ms. Configuration read: {1} WLSKO-0026=Fail to parse REST response from WLS. Json response is {0}. Exception is {1} -WLSKO-0027=Service URL is {0} WLSKO-0028=No servers configured in WebLogic cluster with name {0} WLSKO-0029=Verifying that operator service account can access required operations on required resources in namespace {0} WLSKO-0030=Access denied for operator service account for operation {0} on resource {1} -WLSKO-0031=A namespace has not been created for the Oracle WebLogic Server Operator for Kubernetes -WLSKO-0034=Starting Operator Liveness Thread -WLSKO-0035=Could not create Operator Liveness file /operator/.alive +WLSKO-0034=Starting operator liveness Thread +WLSKO-0035=Could not create operator liveness file /operator/.alive WLSKO-0037=The request must contain an ''Authorization'' header whose value is ''Bearer '' where is a Kubernetes access token WLSKO-0038=Creating or updating Kubernetes presence for WebLogic Domain with UID: {0} WLSKO-0039=Watch event triggered for WebLogic Domain with UID: {0} @@ -62,7 +61,7 @@ WLSKO-0094=Async call {0} invoking: {1}, namespace: {2}, name: {3}, body: {4}, f WLSKO-0095=Async call {0} failed: {1}, code: {2}, headers {3} after invoking {4}, namespace: {5}, name: {6}, body: {7}, fieldSelector: {8}, labelSelector: {9}, resourceVersion: {10}, response body: {11} WLSKO-0096=Async call {0} invoking: {1} succeeded: {2}, code: {3}, headers {4} WLSKO-0097=Async call will not be retried: {0}, code: {1}, headers {2} -WLSKO-0098=Async call {0} will be retried after delay: {1} ms: {2}, namaespace: {3}, name: {4} +WLSKO-0098=Async call {0} will be retried after delay: {1} ms: {2}, namespace: {3}, name: {4} WLSKO-0099=Async call {0} timed-out: {1}, namespace: {2}, name: {3}, body: {4}, fieldSelector: {5}, labelSelector: {6}, resourceVersion: {7} WLSKO-0101=Watch event: {0}, object: {1} WLSKO-0102=Status for Domain with UID {0} is now: {1} @@ -87,7 +86,7 @@ WLSKO-0134=Loading scripts into domain control config map for namespace: {0} WLSKO-0136=Job {0} has failed WLSKO-0137=Job for domain with domainUID {0} in namespace {1} and with job name {2} deleted WLSKO-0138=Waiting for job {0} to be complete -WLSKO-0139=Domain Introspector job {0} created +WLSKO-0139=Domain introspector job {0} created WLSKO-0140=Job {0} is completed with status: {1} WLSKO-0141=Failed to parse WebLogic Domain topology due to exception: {0} WLSKO-0142=Failed to parse results from domain introspector for domain {0} due to exception: {1} @@ -137,7 +136,6 @@ WLSKO-0184=Existing Pod Disruption Budget is correct for WebLogic domain with UI WLSKO-0185=Patching Pod Disruption Budget for WebLogic domain with UID: {0}. Cluster name: {1}. WLSKO-0186=Start managing namespace {0} WLSKO-0187=Stop managing namespace {0} -WLSKO-0188=Model in Image domain with DomainUID ''{0}'' and server name ''{1}'' online updated successfully. No restart is necessary WLSKO-0189=HTTP request method {0} to {1} failed with exception {2}. # Domain status messages @@ -168,7 +166,7 @@ WLSDO-0020=Online WebLogic configuration updates complete \ but there are pending non-dynamic changes that require \ pod restarts to take effect. The changes are: WLSDO-0021=DomainUID ''{0}'' server ''{1}'' does not have a port available for the operator to send REST calls. \ - The default listen port and SSL port are disabled, the admin port is not configured and there is no channel with admin privileges.\ + The default listen port and SSL port are disabled, the admin port is not configured and there is no channel with admin privileges. oneEnvVar=variable multipleEnvVars=variables diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/CrdHelperTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/CrdHelperTest.java index 5bcd87f3cb8..b9086880ce7 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/CrdHelperTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/CrdHelperTest.java @@ -108,6 +108,7 @@ public void setUp() throws Exception { mementos.add(testSupport.install()); mementos.add(StaticStubSupport.install(FileGroupReader.class, "uriToPath", pathFunction)); mementos.add(StaticStubSupport.install(CrdHelper.class, "uriToPath", pathFunction)); + mementos.add(TuningParametersStub.install()); defaultCrd = defineDefaultCrd(); } diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainStatusPatchTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainStatusPatchTest.java index ed39bc5db5f..f4cde1bb1d8 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainStatusPatchTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainStatusPatchTest.java @@ -392,13 +392,14 @@ public void withSubsystemHealthWhenOnlyNewStatusHasSubsystemValues_addThem() { assertThat(builder.getPatches(), hasItemsInOrder( "ADD /status/servers/0/health " - + "{'activationTime':'" + activationTime + + "{'activationTime':'" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(activationTime) + "','overallHealth':'Confused','subsystems':[{'health':'confused','subsystemName':'ejb'}]}", "REPLACE /status/servers/1/health/overallHealth 'Lagging'", "ADD /status/servers/1/health/subsystems/- {'health':'slow','subsystemName':'web'}", "ADD /status/servers/- " + "{'health':" - + "{'activationTime':'" + activationTime + "','overallHealth':'Broken'," + + "{'activationTime':'" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(activationTime) + + "','overallHealth':'Broken'," + "'subsystems':[" + "{'health':'obsolete','subsystemName':'jmx'}," + "{'health':'uninitialized','subsystemName':'sockets'}" diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/TuningParametersStub.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/TuningParametersStub.java index c2f9fb48299..3cddec8c6dc 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/TuningParametersStub.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/TuningParametersStub.java @@ -3,6 +3,7 @@ package oracle.kubernetes.operator.helpers; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -16,6 +17,9 @@ import static com.meterware.simplestub.Stub.createStrictStub; public abstract class TuningParametersStub implements TuningParameters { + public static final String ENABLED_FEATURE = "TestFeature"; + public static final String DISABLED_FEATURE = "OtherFeature"; + // Pod tuning static final int READINESS_INITIAL_DELAY = 1; static final int READINESS_TIMEOUT = 2; @@ -93,4 +97,9 @@ public String put(String key, String value) { public Set> entrySet() { return namedParameters.entrySet(); } + + @Override + public FeatureGates getFeatureGates() { + return new FeatureGates(Collections.singletonList(ENABLED_FEATURE)); + } } diff --git a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGeneratorTest.java b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGeneratorTest.java index 2740b0614bc..1cccb0f0b62 100644 --- a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGeneratorTest.java +++ b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/CrdSchemaGeneratorTest.java @@ -6,6 +6,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -16,10 +18,16 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.meterware.simplestub.Memento; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion; import com.networknt.schema.ValidationMessage; +import oracle.kubernetes.json.Feature; +import oracle.kubernetes.operator.helpers.TuningParametersStub; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -28,9 +36,24 @@ import static oracle.kubernetes.weblogic.domain.model.DomainTestBase.DOMAIN_V2_SAMPLE_YAML_3; import static oracle.kubernetes.weblogic.domain.model.DomainTestBase.DOMAIN_V2_SAMPLE_YAML_4; import static oracle.kubernetes.weblogic.domain.model.DomainTestBase.DOMAIN_V2_SAMPLE_YAML_5; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.junit.MatcherAssert.assertThat; public class CrdSchemaGeneratorTest { + private final List mementos = new ArrayList<>(); + + @BeforeEach + public void setUp() throws Exception { + mementos.add(TuningParametersStub.install()); + } + + @AfterEach + public void tearDown() { + mementos.forEach(Memento::revert); + } + @ParameterizedTest @ValueSource(strings = {DOMAIN_V2_SAMPLE_YAML, DOMAIN_V2_SAMPLE_YAML_2, DOMAIN_V2_SAMPLE_YAML_3, DOMAIN_V2_SAMPLE_YAML_4, DOMAIN_V2_SAMPLE_YAML_5}) @@ -44,6 +67,35 @@ void validateSchemaAgainstSamples(String fileName) throws IOException { } } + @Test + public void whenMixOfEnabledDisabledFeatures_validateSchemaOnlyContainsEnabled() { + final Map schema = createSchema(SomeObject.class); + + assertThat(schema, hasKey("properties")); + @SuppressWarnings("unchecked") + Map properties = (Map) schema.get("properties"); + assertThat(properties, hasKey("enabled")); + assertThat(properties, not(hasKey("disabled"))); + } + + private static class SomeObject { + @Feature(TuningParametersStub.ENABLED_FEATURE) + private ClassForEnabledFeature enabled; + @Feature(TuningParametersStub.DISABLED_FEATURE) + private ClassForDisabledFeature disabled; + private String five; + } + + private static class ClassForEnabledFeature { + private String one; + private String two; + } + + private static class ClassForDisabledFeature { + private String three; + private String four; + } + @Nonnull private String toExceptionMessage(String fileName, Set validationResult) { return Stream.concat(toPrefixStream(fileName), toFailureStream(validationResult)) @@ -65,10 +117,14 @@ private String toIndentedString(ValidationMessage message) { return " " + message; } + private static Map createSchema(Class someClass) { + return CrdSchemaGenerator.createCrdSchemaGenerator().generate(someClass); + } + @SuppressWarnings("SameParameterValue") private static JsonSchema createJsonSchema(Class someClass) throws JsonProcessingException { final JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909); - final Map schema = CrdSchemaGenerator.createCrdSchemaGenerator().generate(someClass); + final Map schema = createSchema(someClass); return schemaFactory.getSchema(toJsonInputStream(schema)); }