Skip to content
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

feat: update to informer configuration changes for controller #895

Merged
merged 12 commits into from
Jul 4, 2024
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
4 changes: 2 additions & 2 deletions .github/project.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Java Operator SDK Extension
release:
current-version: 6.6.8
next-version: 6.6.9-SNAPSHOT
current-version: 6.7.1
next-version: 6.7.2-SNAPSHOT

59 changes: 57 additions & 2 deletions .github/workflows/build-for-quarkus-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ on:
description: 'Quarkus PR number to use to run a QOSDK build with'
type: string
required: false
fkc-pr:
description: 'Fabric8 client PR number to use to run a QOSDK build with'
type: string
required: false
fkc-version:
description: 'Fabric8 client version to run a QOSDK build with'
type: string
required: false
quarkus-version:
type: string
required: false
description: 'Quarkus version to use'
java-version:
description: 'Java version to build with'
type: string
Expand All @@ -59,6 +71,11 @@ on:
type: string
required: false
default: 'integration-tests,samples'
profiles:
description: 'Maven profiles to apply to build'
type: string
required: false
default: 'use-snapshots'

concurrency:
group: "${{ inputs.quarkus-version }}-${{inputs.java-version}}-${{ github.ref }}"
Expand Down Expand Up @@ -129,6 +146,25 @@ jobs:
mvn -Dquickly
cd -

- name: Check-out Fabric8 client PR if requested
uses: actions/checkout@v4
if: "${{ inputs.fkc-pr != '' }}"
with:
repository: fabric8io/kubernetes-client
path: fkc

- name: Build Fabric8 client PR if requested
if: "${{ inputs.fkc-pr != '' }}"
id: build-fkc-pr
run: |
cd fkc
git fetch origin pull/${{ github.event.inputs.fkc-pr }}/head:pr-to-check
git switch pr-to-check
mvn versions:set -DnewVersion=999.${{ github.event.inputs.fkc-pr }}-SNAPSHOT versions:commit
echo "f8_pr_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT
mvn clean install -DskipTests
cd -

- name: Retrieve Quarkus version from platform
if: "${{ inputs.quarkus-version == '' && inputs.quarkus-pr == ''}}"
id: get-quarkus-version
Expand Down Expand Up @@ -166,18 +202,37 @@ jobs:
echo "Using Quarkus ${{ steps.quarkus-version.outputs.quarkus_version }}"
mvn versions:set-property -P'${{steps.set-mvn-profiles.outputs.maven_profiles}}' -Dproperty=quarkus.version -DnewVersion=${{ steps.quarkus-version.outputs.quarkus_version }}

- name: Change Fabric8 client version
id: fkc-version
if: "${{ inputs.fkc-pr != '' || inputs.fkc-version != '' }}"
run: |
if [[ -n "${{ inputs.fkc-version }}" ]]; then
fkc_version="${{ inputs.fkc-version }}"
fi
if [[ -n "${{ inputs.fkc-pr }}" ]]; then
fkc_version=${{ steps.build-fkc-pr.outputs.f8_pr_version }}
fi
echo "Using Fabric8 ${fkc_version}"
mvn versions:set-property -P'${{steps.set-mvn-profiles.outputs.maven_profiles}}' -Dproperty=fabric8-client.version -DnewVersion=${fkc_version}
echo "fkc_version=${fkc_version}" >> $GITHUB_OUTPUT

- name: Output versions being used
run: |
echo "QOSDK version: $(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)"
echo "JOSDK version: $(mvn help:evaluate -Dexpression=java-operator-sdk.version -q -DforceStdout)"
echo "JOSDK Fabric8 version: ${{ steps.build-josdk-pr.outputs.josdk_f8_version }}"
echo "JOSDK overridden Fabric8 version ${{ steps.fkc-version.outputs.fkc_version }}"
echo "Quarkus version: $(mvn help:evaluate -Dexpression=quarkus.version -q -DforceStdout)"
echo "Quarkus Fabric8 version: ${{ steps.build-quarkus-pr.outputs.quarkus_f8_version }}"
echo "Effective Fabric8 version: $(mvn dependency:tree -Dincludes=io.fabric8:kubernetes-client-api -pl core/deployment | grep io.fabric8:kubernetes-client-api -m1 | cut -d ':' -f 4)"
echo "Effective Fabric8 version: $(mvn dependency:tree -P'${{steps.set-mvn-profiles.outputs.maven_profiles}}' -Dincludes=io.fabric8:kubernetes-client-api -pl core/deployment | grep io.fabric8:kubernetes-client-api -m1 | cut -d ':' -f 4)"

- name: Build with Maven (JVM)
run: mvn -B formatter:validate install -P'${{steps.set-mvn-profiles.outputs.maven_profiles}}' --file pom.xml

- name: Dependency tree on failure
if: failure()
run: mvn -B dependency:tree -Dverbose

- name: Kubernetes KinD Cluster
uses: container-tools/kind-action@v2
with:
Expand All @@ -193,7 +248,7 @@ jobs:
- name: Install Operator Lifecycle Manager and Operator SDK into Kind
run: operator-sdk olm install --version v0.23.0

# Joke sample currently doesn't validate with OLM v1 because it bundles required Joke CRD, which v1 thinks should be owned
# Joke sample currently doesn't validate with OLM v1 because it bundles required Joke CRD, which v1 thinks should be owned
- name: Validate OLM bundles (excluding Joke sample)
run: |
cd samples/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.InformerConfig;
import io.quarkiverse.operatorsdk.annotations.CSVMetadata;
import io.quarkiverse.operatorsdk.annotations.RBACRule;

@CSVMetadata(bundleName = "second-operator")
@RBACRule(apiGroups = SecondReconciler.RBAC_RULE_GROUP, resources = SecondReconciler.RBAC_RULE_RES, verbs = SecondReconciler.RBAC_RULE_VERBS)
@ControllerConfiguration(namespaces = "foo")
@ControllerConfiguration(informerConfig = @InformerConfig(namespaces = "foo"))
public class SecondReconciler implements Reconciler<Second> {
public static final String RBAC_RULE_GROUP = "halkyon.io";
public static final String RBAC_RULE_RES = "SomeResource";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.jboss.jandex.*;
Expand All @@ -24,6 +25,7 @@
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
import io.javaoperatorsdk.operator.api.reconciler.MaxReconciliationInterval;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.InformerConfigHolder;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentConverter;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
Expand Down Expand Up @@ -67,9 +69,13 @@ public KubernetesDependentResourceConfig configFrom(KubernetesDependent configAn
// make the configuration bytecode-serializable
return new QuarkusKubernetesDependentResourceConfig(
original.useSSA(), original.createResourceOnlyIfNotExistingWithSSA(),
new QuarkusKubernetesDependentInformerConfig(original.informerConfig()));
new QuarkusInformerConfigHolder(original.informerConfig()));
}
};
private static final Supplier<AnnotationInstance> NULL_ANNOTATION_SUPPLIER = () -> null;
public static final Supplier<String[]> NULL_STRING_ARRAY_SUPPLIER = () -> null;
public static final Supplier<String> NULL_STRING_SUPPLIER = () -> null;

static {
// register Quarkus-specific converter for Kubernetes dependent resources
DependentResourceConfigurationResolver.registerConverter(KubernetesDependentResource.class,
Expand Down Expand Up @@ -159,10 +165,13 @@ static QuarkusControllerConfiguration createConfiguration(
Long nullableInformerListLimit = null;
String fieldManager = null;
ItemStore<?> itemStore = null;
Set<String> namespaces = null;
String informerName = null;
String labelSelector = null;
if (controllerAnnotation != null) {
final var intervalFromAnnotation = ConfigurationUtils.annotationValueOrDefault(
controllerAnnotation, "maxReconciliationInterval", AnnotationValue::asNested,
() -> null);
NULL_ANNOTATION_SUPPLIER);
final var interval = ConfigurationUtils.annotationValueOrDefault(
intervalFromAnnotation, "interval", AnnotationValue::asLong,
() -> MaxReconciliationInterval.DEFAULT_INTERVAL);
Expand All @@ -175,32 +184,53 @@ static QuarkusControllerConfiguration createConfiguration(
maxReconciliationInterval = Duration.of(interval, timeUnit.toChronoUnit());
}

onAddFilter = ConfigurationUtils.instantiateImplementationClass(
controllerAnnotation, "onAddFilter", OnAddFilter.class, OnAddFilter.class, true, index);
onUpdateFilter = ConfigurationUtils.instantiateImplementationClass(
controllerAnnotation, "onUpdateFilter", OnUpdateFilter.class, OnUpdateFilter.class,
true, index);
genericFilter = ConfigurationUtils.instantiateImplementationClass(
controllerAnnotation, "genericFilter", GenericFilter.class, GenericFilter.class,
true, index);
retryClass = ConfigurationUtils.annotationValueOrDefault(controllerAnnotation,
"retry", av -> loadClass(av.asClass().name().toString(), Retry.class), () -> GenericRetry.class);
// deal with informer configuration
final var informerConfigAnnotation = ConfigurationUtils.annotationValueOrDefault(controllerAnnotation,
"informerConfig", AnnotationValue::asNested, NULL_ANNOTATION_SUPPLIER);
if (informerConfigAnnotation != null) {
onAddFilter = ConfigurationUtils.instantiateImplementationClass(
informerConfigAnnotation, "onAddFilter", OnAddFilter.class, OnAddFilter.class, true, index);
onUpdateFilter = ConfigurationUtils.instantiateImplementationClass(
informerConfigAnnotation, "onUpdateFilter", OnUpdateFilter.class, OnUpdateFilter.class,
true, index);
genericFilter = ConfigurationUtils.instantiateImplementationClass(
informerConfigAnnotation, "genericFilter", GenericFilter.class, GenericFilter.class,
true, index);
retryClass = ConfigurationUtils.annotationValueOrDefault(informerConfigAnnotation,
"retry", av -> loadClass(av.asClass().name().toString(), Retry.class), () -> GenericRetry.class);
nullableInformerListLimit = ConfigurationUtils.annotationValueOrDefault(
informerConfigAnnotation, "informerListLimit", AnnotationValue::asLong,
() -> null);
itemStore = ConfigurationUtils.instantiateImplementationClass(informerConfigAnnotation, "itemStore",
ItemStore.class,
ItemStore.class, true, index);
informerName = ConfigurationUtils.annotationValueOrDefault(informerConfigAnnotation, "name",
AnnotationValue::asString, NULL_STRING_SUPPLIER);
labelSelector = ConfigurationUtils.annotationValueOrDefault(informerConfigAnnotation,
"labelSelector",
AnnotationValue::asString,
NULL_STRING_SUPPLIER);

// extract the namespaces
// first check if we explicitly set the namespaces via the annotations
namespaces = Optional.ofNullable(informerConfigAnnotation.value("namespaces"))
.map(v -> new HashSet<>(Arrays.asList(v.asStringArray())))
.orElse(null);
}

final var retryConfigurableInfo = configurableInfos.get(retryClass.getName());
retryConfigurationClass = getConfigurationAnnotationClass(reconcilerInfo, retryConfigurableInfo);

rateLimiterClass = ConfigurationUtils.annotationValueOrDefault(
controllerAnnotation,
"rateLimiter", av -> loadClass(av.asClass().name().toString(), RateLimiter.class),
() -> DefaultRateLimiter.class);
final var rateLimiterConfigurableInfo = configurableInfos.get(rateLimiterClass.getName());
rateLimiterConfigurationClass = getConfigurationAnnotationClass(reconcilerInfo,
rateLimiterConfigurableInfo);
nullableInformerListLimit = ConfigurationUtils.annotationValueOrDefault(
controllerAnnotation, "informerListLimit", AnnotationValue::asLong,
() -> null);

fieldManager = ConfigurationUtils.annotationValueOrDefault(controllerAnnotation, "fieldManager",
AnnotationValue::asString, () -> null);
itemStore = ConfigurationUtils.instantiateImplementationClass(controllerAnnotation, "itemStore", ItemStore.class,
ItemStore.class, true, index);
AnnotationValue::asString, NULL_STRING_SUPPLIER);
}

// check if we have additional RBAC rules to handle
Expand All @@ -209,14 +239,6 @@ static QuarkusControllerConfiguration createConfiguration(
// check if we have additional RBAC role refs to handle
final var additionalRBACRoleRefs = extractAdditionalRBACRoleRefs(info);

// extract the namespaces
// first check if we explicitly set the namespaces via the annotations
Set<String> namespaces = null;
if (controllerAnnotation != null) {
namespaces = Optional.ofNullable(controllerAnnotation.value("namespaces"))
.map(v -> new HashSet<>(Arrays.asList(v.asStringArray())))
.orElse(null);
}
// remember whether or not we explicitly set the namespaces
final boolean wereNamespacesSet;
if (namespaces == null) {
Expand All @@ -236,26 +258,36 @@ static QuarkusControllerConfiguration createConfiguration(
// create the configuration
final ReconciledAugmentedClassInfo<?> primaryInfo = reconcilerInfo.associatedResourceInfo();
final var primaryAsResource = primaryInfo.asResourceTargeting();
final var resourceClass = primaryInfo.loadAssociatedClass();
final Class<? extends HasMetadata> resourceClass = (Class<? extends HasMetadata>) primaryInfo.loadAssociatedClass();
final String resourceFullName = primaryAsResource.fullResourceName();

final var informerConfigHolder = InformerConfigHolder.builder(resourceClass)
.withName(informerName)
.withNamespaces(namespaces)
.withLabelSelector(labelSelector)
.withGenericFilter(genericFilter)
.withOnAddFilter(onAddFilter)
.withOnUpdateFilter(onUpdateFilter)
.withItemStore(itemStore)
.withInformerListLimit(nullableInformerListLimit)
.buildForController();
final var informerConfig = new QuarkusInformerConfigHolder(informerConfigHolder);

configuration = new QuarkusControllerConfiguration(
reconcilerClassName,
name,
resourceFullName,
primaryAsResource.version(),
configExtractor.generationAware(),
resourceClass,
nullableInformerListLimit,
namespaces,
wereNamespacesSet,
getFinalizer(controllerAnnotation, resourceFullName),
getLabelSelector(controllerAnnotation),
primaryAsResource.hasNonVoidStatus(),
maxReconciliationInterval,
onAddFilter, onUpdateFilter, genericFilter, retryClass, retryConfigurationClass, rateLimiterClass,
retryClass, retryConfigurationClass, rateLimiterClass,
rateLimiterConfigurationClass, additionalRBACRules, additionalRBACRoleRefs,
fieldManager, itemStore);
fieldManager,
informerConfig);

// compute workflow and set it
initializeWorkflowIfNeeded(configuration, reconcilerInfo, index);
Expand Down Expand Up @@ -341,27 +373,27 @@ private static PolicyRule extractRule(AnnotationInstance ruleAnnotation) {
builder.withApiGroups(ConfigurationUtils.annotationValueOrDefault(ruleAnnotation,
"apiGroups",
AnnotationValue::asStringArray,
() -> null));
NULL_STRING_ARRAY_SUPPLIER));

builder.withVerbs(ConfigurationUtils.annotationValueOrDefault(ruleAnnotation,
"verbs",
AnnotationValue::asStringArray,
() -> null));
NULL_STRING_ARRAY_SUPPLIER));

builder.withResources(ConfigurationUtils.annotationValueOrDefault(ruleAnnotation,
"resources",
AnnotationValue::asStringArray,
() -> null));
NULL_STRING_ARRAY_SUPPLIER));

builder.withResourceNames(ConfigurationUtils.annotationValueOrDefault(ruleAnnotation,
"resourceNames",
AnnotationValue::asStringArray,
() -> null));
NULL_STRING_ARRAY_SUPPLIER));

builder.withNonResourceURLs(ConfigurationUtils.annotationValueOrDefault(ruleAnnotation,
"nonResourceURLs",
AnnotationValue::asStringArray,
() -> null));
NULL_STRING_ARRAY_SUPPLIER));

return builder.build();
}
Expand All @@ -378,7 +410,7 @@ private static RoleRef extractRoleRef(AnnotationInstance roleRefAnnotation) {
builder.withName(ConfigurationUtils.annotationValueOrDefault(roleRefAnnotation,
"name",
AnnotationValue::asString,
() -> null));
NULL_STRING_SUPPLIER));

return builder.build();
}
Expand Down Expand Up @@ -440,7 +472,7 @@ private static DependentResourceSpecMetadata createDependentResourceSpec(

final var useEventSourceWithName = ConfigurationUtils.annotationValueOrDefault(
dependentConfig, "useEventSourceWithName", AnnotationValue::asString,
() -> null);
NULL_STRING_SUPPLIER);

final var spec = new DependentResourceSpecMetadata(dependentClass, dependent.nameOrFailIfUnset(),
dependsOn, readyCondition, reconcilePrecondition, deletePostcondition, activationCondition,
Expand All @@ -457,11 +489,4 @@ private static String getFinalizer(AnnotationInstance controllerAnnotation, Stri
AnnotationValue::asString,
() -> ReconcilerUtils.getDefaultFinalizerName(crdName));
}

private static String getLabelSelector(AnnotationInstance controllerAnnotation) {
return ConfigurationUtils.annotationValueOrDefault(controllerAnnotation,
"labelSelector",
AnnotationValue::asString,
() -> null);
}
}
Loading
Loading