├── docs ├── modules │ └── ROOT │ │ ├── examples │ │ └── .keepme │ │ ├── assets │ │ └── images │ │ │ └── .keepme │ │ ├── pages │ │ ├── includes │ │ │ └── attributes.adoc │ │ ├── config.adoc │ │ ├── upgrade.adoc │ │ └── deploy-with-helm.adoc │ │ └── nav.adoc ├── antora.yml └── templates │ └── includes │ └── attributes.adoc ├── common ├── src │ └── main │ │ ├── resources │ │ ├── META-INF │ │ │ └── beans.xml │ │ └── templates │ │ │ └── cli │ │ │ └── api │ │ │ ├── spec.qute │ │ │ ├── status.qute │ │ │ ├── resource.qute │ │ │ └── reconciler.qute │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── common │ │ ├── CLIConstants.java │ │ └── ClassLoadingUtils.java └── pom.xml ├── cli └── src │ └── main │ ├── resources │ └── application.properties │ └── java │ └── io │ └── quarkiverse │ └── operatorsdk │ └── cli │ ├── QOSDKCommand.java │ └── API.java ├── contributing └── eclipse.importorder ├── samples ├── joke │ └── src │ │ └── main │ │ ├── kubernetes │ │ └── icon.png │ │ ├── k8s │ │ └── jokerequest.yml │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── samples │ │ └── joke │ │ ├── JokeRequest.java │ │ ├── JokeService.java │ │ └── JokeModel.java ├── pingpong │ └── src │ │ ├── main │ │ ├── k8s │ │ │ └── pingrequest.yml │ │ ├── kubernetes │ │ │ └── pingpong-operator.icon.png │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── samples │ │ │ └── pingpong │ │ │ ├── PingPongOperatorCSVMetadata.java │ │ │ ├── Status.java │ │ │ ├── Ping.java │ │ │ └── Pong.java │ │ └── test │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── samples │ │ └── pingpong │ │ └── AssertGeneratedResourcesIT.java ├── mysql-schema │ └── src │ │ └── main │ │ ├── resources │ │ ├── mydb.yml │ │ └── application.properties │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── samples │ │ └── mysqlschema │ │ ├── SchemaSpec.java │ │ ├── dependent │ │ └── ResourcePollerConfig.java │ │ ├── MySQLSchema.java │ │ ├── SchemaStatus.java │ │ └── schema │ │ └── Schema.java ├── exposedapp │ └── src │ │ ├── main │ │ ├── resources │ │ │ ├── app.yml │ │ │ └── application.properties │ │ └── java │ │ │ └── io │ │ │ └── halkyon │ │ │ ├── ExposedApp.java │ │ │ ├── ExposedAppSpec.java │ │ │ ├── ExposedAppStatus.java │ │ │ └── ServiceDependent.java │ │ └── test │ │ └── java │ │ └── io │ │ └── halkyon │ │ └── NativeExposedAppReconcilerIT.java └── external-crd │ ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── ExternalCRDReconciler.java │ └── test │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── it │ │ └── ExternalCRDTest.java │ └── external-crds │ └── external.crd.yml ├── core ├── runtime │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ ├── services │ │ │ │ └── io.quarkus.dev.spi.HotReplacementSetup │ │ │ │ └── quarkus-extension.yaml │ │ ├── java-templates │ │ │ └── io │ │ │ │ └── quarkiverse │ │ │ │ └── operatorsdk │ │ │ │ └── runtime │ │ │ │ └── Versions.java │ │ └── java │ │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── runtime │ │ │ ├── NoOpMetricsProvider.java │ │ │ ├── Constants.java │ │ │ ├── KubernetesClientObjectMapperCustomizer.java │ │ │ ├── ExternalGradualRetryConfiguration.java │ │ │ ├── ExternalGradualRetryIntervalConfiguration.java │ │ │ ├── MicrometerMetricsProvider.java │ │ │ ├── devmode │ │ │ └── OperatorSDKHotReplacementSetup.java │ │ │ ├── devui │ │ │ └── JSONRPCService.java │ │ │ ├── CRDInfo.java │ │ │ ├── devconsole │ │ │ └── EventSourceInfo.java │ │ │ ├── api │ │ │ └── ConfigurableReconciler.java │ │ │ ├── QuarkusKubernetesDependentResourceConfig.java │ │ │ ├── CRDGenerationInfo.java │ │ │ └── OperatorHealthCheck.java │ │ └── test │ │ └── resources │ │ └── external.crd.yml ├── deployment │ └── src │ │ ├── test │ │ ├── java │ │ │ └── io │ │ │ │ └── quarkiverse │ │ │ │ └── operatorsdk │ │ │ │ └── test │ │ │ │ ├── sources │ │ │ │ ├── Foo.java │ │ │ │ ├── SimpleSpec.java │ │ │ │ ├── SimpleStatus.java │ │ │ │ ├── SimpleSpecV2.java │ │ │ │ ├── InjectedDependency.java │ │ │ │ ├── TestCR.java │ │ │ │ ├── CRUDConfigMap.java │ │ │ │ ├── ExternalV1.java │ │ │ │ ├── SimpleCRV2.java │ │ │ │ ├── SimpleReconcilerV2.java │ │ │ │ ├── External.java │ │ │ │ ├── SimpleCR.java │ │ │ │ ├── NonKubeResource.java │ │ │ │ ├── LabelAdderCRDPostProcessor.java │ │ │ │ ├── NoDefaultArgConstructorDependent.java │ │ │ │ ├── ReadOnlySecret.java │ │ │ │ ├── UnownedReconciler.java │ │ │ │ ├── TypelessKubeResource.java │ │ │ │ ├── TypelessAnotherKubeResource.java │ │ │ │ ├── OneNSReconciler.java │ │ │ │ ├── ConfiguredReconciler.java │ │ │ │ ├── CreateOnlyService.java │ │ │ │ ├── WatchAllNamespacesReconciler.java │ │ │ │ ├── WatchAllReconciler.java │ │ │ │ ├── TestReconciler.java │ │ │ │ └── SimpleReconciler.java │ │ │ │ ├── OperatorSDKDevModeTest.java │ │ │ │ ├── DefaultConfigurationTest.java │ │ │ │ ├── OneNSConfigurationTest.java │ │ │ │ ├── UnownedTest.java │ │ │ │ └── NoCRDGenerationTest.java │ │ └── resources │ │ │ └── application.properties │ │ └── main │ │ ├── resources │ │ ├── helm │ │ │ ├── static │ │ │ │ ├── values.schema.json │ │ │ │ ├── serviceaccount.yaml │ │ │ │ ├── README.md │ │ │ │ ├── generic-crd-cluster-role.yaml │ │ │ │ ├── service.yaml │ │ │ │ └── generic-crd-cluster-role-binding.yaml │ │ │ └── additional-crd-role-binding-template.yaml │ │ └── dev-ui │ │ │ ├── logo_light.svg │ │ │ └── logo_dark.svg │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── deployment │ │ ├── VersionBuildItem.java │ │ ├── UnownedCRDInfoBuildItem.java │ │ ├── CRDGenerator.java │ │ ├── GeneratedCRDInfoBuildItem.java │ │ ├── BuildTimeConfigurationServiceBuildItem.java │ │ ├── ReconcilerInfosBuildItem.java │ │ ├── AnnotationConfigurablesBuildItem.java │ │ ├── QOSDKReflectiveClassBuildItem.java │ │ ├── AddNamespaceEnvVarBuildStep.java │ │ ├── ControllerConfigurationsBuildItem.java │ │ ├── devui │ │ ├── commands │ │ │ ├── ConsoleCommands.java │ │ │ └── VersionsCommand.java │ │ └── DevUIProcessor.java │ │ ├── BuildTimeConfigurationServiceBuildStep.java │ │ ├── ReflectionRegistrations.java │ │ └── GeneratedKubernetesManifestsProcessor.java └── pom.xml ├── integration-tests ├── cdi │ └── src │ │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── it │ │ └── cdi │ │ ├── TestUUIDBean.java │ │ ├── TestResource.java │ │ ├── DeletePostCondition.java │ │ ├── ReconcilePrecondition.java │ │ ├── CustomActionvationCondition.java │ │ ├── ReadyPostCondition.java │ │ ├── TestReconciler.java │ │ └── DeploymentDependent.java ├── basic │ └── src │ │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── it │ │ │ ├── NativeOperatorSDKResourceIT.java │ │ │ ├── EmptyOperatorLevelNamespacesTestProfile.java │ │ │ ├── SetOperatorLevelNamespacesTestProfile.java │ │ │ ├── CustomKubernetesServerTestResource.java │ │ │ └── EmptyOperatorLevelNamespacesTest.java │ │ └── main │ │ ├── java │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── it │ │ │ ├── CustomRateConfiguration.java │ │ │ ├── Test.java │ │ │ ├── ChildTestResource2.java │ │ │ ├── ChildTestResource.java │ │ │ ├── Keycloak.java │ │ │ ├── TestResource.java │ │ │ ├── ADRConfigurationAnnotation.java │ │ │ ├── TestLeaderElectionConfiguration.java │ │ │ ├── ADRConfiguration.java │ │ │ ├── EmptyCR.java │ │ │ ├── GatewayReconciler.java │ │ │ ├── ShouldBeIgnoredReconciler2.java │ │ │ ├── IgnoredByAnnotationReconciler.java │ │ │ ├── TestMetrics.java │ │ │ ├── ShouldBeIgnoredReconciler.java │ │ │ ├── NameWithSpaceReconciler.java │ │ │ ├── ApplicationScopedReconciler.java │ │ │ ├── EmptyReconciler.java │ │ │ ├── KeycloakController.java │ │ │ ├── VariableNSReconciler.java │ │ │ ├── TestReconciler.java │ │ │ ├── CustomRateLimiterReconciler.java │ │ │ ├── NamespaceFromEnvReconciler.java │ │ │ ├── AnnotatedDependentReconciler.java │ │ │ ├── CRUDDependentResource.java │ │ │ ├── ConfiguredReconciler.java │ │ │ ├── DependentDefiningReconciler.java │ │ │ ├── CustomRateLimiter.java │ │ │ ├── NullItemStore.java │ │ │ └── SecretReconciler.java │ │ └── resources │ │ └── application.properties └── pom.xml ├── .github ├── project.yml ├── workflows │ ├── pre-release.yml │ ├── release-snapshot-on-push-selected-branches.yml │ ├── release-snapshot-nightly-next-fabric8-version.yml │ ├── release-prepare.yml │ └── release-perform.yml ├── dependabot.yml ├── scripts │ ├── testJokeSample.sh │ ├── testPingPongSample.sh │ └── waitFor.sh └── CODEOWNERS ├── bundle-generator ├── deployment │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.smallrye.config.SmallRyeConfigBuilderCustomizer │ │ └── java │ │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── bundle │ │ │ └── deployment │ │ │ ├── GeneratedBundleBuildItem.java │ │ │ ├── CSVMetadataBuildItem.java │ │ │ ├── KubernetesLabelConfigOverrider.java │ │ │ └── builders │ │ │ └── CustomResourceManifestsBuilder.java │ │ └── test │ │ ├── java │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── bundle │ │ │ ├── sources │ │ │ ├── ExternalDependentResource.java │ │ │ ├── PodDependentResource.java │ │ │ ├── OptionalExplicitNativeDependent.java │ │ │ ├── ReconcilerWithNoCsvMetadata.java │ │ │ ├── First.java │ │ │ ├── Second.java │ │ │ ├── CReconciler.java │ │ │ ├── SecondExternal.java │ │ │ ├── External.java │ │ │ ├── BReconciler.java │ │ │ ├── DuplicatedBundleNameWithoutSharedCSVMetadata1.java │ │ │ ├── Third.java │ │ │ ├── DuplicatedBundleNameWithoutSharedCSVMetadata2.java │ │ │ ├── V1Beta1CRD.java │ │ │ ├── FirstReconciler.java │ │ │ ├── OptionalImplicitNativeDependent.java │ │ │ ├── AReconciler.java │ │ │ ├── ReconcilerWithExternalCR.java │ │ │ └── SecondReconciler.java │ │ │ └── OutputLabelSelectorsIfRequestedTest.java │ │ └── external-crds │ │ ├── external.crd.yml │ │ └── v1beta1spec.crd.yml ├── runtime │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── quarkus-extension.yaml │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── bundle │ │ └── runtime │ │ ├── BundleConfiguration.java │ │ └── BundleGenerationConfiguration.java └── pom.xml ├── helm ├── deployment │ └── src │ │ └── test │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── helm │ │ └── deployment │ │ └── test │ │ └── sources │ │ ├── SimpleSpec.java │ │ ├── SimpleStatus.java │ │ ├── SimpleCR.java │ │ ├── WatchAllNamespacesReconciler.java │ │ └── SimpleReconciler.java └── pom.xml ├── annotations ├── src │ └── main │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── annotations │ │ ├── SharedCSVMetadata.java │ │ ├── AdditionalRBACRules.java │ │ ├── AdditionalRBACRoleRefs.java │ │ ├── RBACRule.java │ │ ├── RBACRoleRef.java │ │ └── RBACVerbs.java └── pom.xml ├── common-deployment ├── src │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── quarkiverse │ │ │ └── operatorsdk │ │ │ └── common │ │ │ └── ConfigurationUtilsTest.java │ └── main │ │ └── java │ │ └── io │ │ └── quarkiverse │ │ └── operatorsdk │ │ └── common │ │ ├── DeserializedKubernetesResourcesBuildItem.java │ │ ├── FileUtils.java │ │ └── AnnotationConfigurableAugmentedClassInfo.java └── pom.xml ├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── .all-contributorsrc └── CONTRIBUTING.md /docs/modules/ROOT/examples/.keepme: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/modules/ROOT/assets/images/.keepme: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /common/src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.banner.enabled=false -------------------------------------------------------------------------------- /common/src/main/resources/templates/cli/api/spec.qute: -------------------------------------------------------------------------------- 1 | package {packageName}; 2 | 3 | public class {kind}Spec { 4 | } -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/includes/attributes.adoc: -------------------------------------------------------------------------------- 1 | :project-version: 7.4.0 2 | 3 | :examples-dir: ./../examples/ 4 | -------------------------------------------------------------------------------- /common/src/main/resources/templates/cli/api/status.qute: -------------------------------------------------------------------------------- 1 | package {packageName}; 2 | 3 | public class {kind}Status { 4 | } -------------------------------------------------------------------------------- /docs/antora.yml: -------------------------------------------------------------------------------- 1 | name: quarkus-operator-sdk 2 | title: Operator SDK 3 | version: dev 4 | nav: 5 | - modules/ROOT/nav.adoc 6 | -------------------------------------------------------------------------------- /docs/templates/includes/attributes.adoc: -------------------------------------------------------------------------------- 1 | :project-version: ${release.current-version} 2 | 3 | :examples-dir: ./../examples/ 4 | -------------------------------------------------------------------------------- /contributing/eclipse.importorder: -------------------------------------------------------------------------------- 1 | #Organize Import Order 2 | #Wed Jan 23 12:03:29 AEDT 2019 3 | 0=java 4 | 1=javax 5 | 2=jakarta 6 | 3=org 7 | 4=com 8 | -------------------------------------------------------------------------------- /samples/joke/src/main/kubernetes/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkiverse/quarkus-operator-sdk/HEAD/samples/joke/src/main/kubernetes/icon.png -------------------------------------------------------------------------------- /samples/pingpong/src/main/k8s/pingrequest.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "samples.javaoperatorsdk.io/v1alpha1" 2 | kind: Ping 3 | metadata: 4 | name: my-ping-request -------------------------------------------------------------------------------- /core/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup: -------------------------------------------------------------------------------- 1 | io.quarkiverse.operatorsdk.runtime.devmode.OperatorSDKHotReplacementSetup -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/Foo.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | public class Foo { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.kubernetes-client.devservices.override-kubeconfig=true 2 | quarkus.operator-sdk.crd.validate=false 3 | -------------------------------------------------------------------------------- /samples/joke/src/main/k8s/jokerequest.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "samples.javaoperatorsdk.io/v1alpha1" 2 | kind: JokeRequest 3 | metadata: 4 | name: jr-any 5 | spec: 6 | category: Any -------------------------------------------------------------------------------- /samples/mysql-schema/src/main/resources/mydb.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "mysql.sample.javaoperatorsdk/v1" 2 | kind: MySQLSchema 3 | metadata: 4 | name: mydb 5 | spec: 6 | encoding: utf8 -------------------------------------------------------------------------------- /samples/exposedapp/src/main/resources/app.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "halkyon.io/v1alpha1" 2 | kind: ExposedApp 3 | metadata: 4 | name: hello-quarkus 5 | spec: 6 | imageRef: 'nginx:1.14.2' -------------------------------------------------------------------------------- /.github/project.yml: -------------------------------------------------------------------------------- 1 | name: Java Operator SDK Extension 2 | release: 3 | current-version: 7.4.0 4 | next-version: 7.4.1-SNAPSHOT 5 | quarkus-platform-branches: 6 | - "main" 7 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer: -------------------------------------------------------------------------------- 1 | io.quarkiverse.operatorsdk.bundle.deployment.KubernetesLabelConfigOverrider 2 | -------------------------------------------------------------------------------- /samples/pingpong/src/main/kubernetes/pingpong-operator.icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkiverse/quarkus-operator-sdk/HEAD/samples/pingpong/src/main/kubernetes/pingpong-operator.icon.png -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/SimpleSpec.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | public class SimpleSpec { 4 | public String value; 5 | } 6 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/SimpleStatus.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | public class SimpleStatus { 4 | public String value; 5 | } 6 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/SimpleSpecV2.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | public class SimpleSpecV2 { 4 | public String value; 5 | public int number; 6 | } 7 | -------------------------------------------------------------------------------- /helm/deployment/src/test/java/io/quarkiverse/operatorsdk/helm/deployment/test/sources/SimpleSpec.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.helm.deployment.test.sources; 2 | 3 | public class SimpleSpec { 4 | public String value; 5 | } 6 | -------------------------------------------------------------------------------- /helm/deployment/src/test/java/io/quarkiverse/operatorsdk/helm/deployment/test/sources/SimpleStatus.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.helm.deployment.test.sources; 2 | 3 | public class SimpleStatus { 4 | public String value; 5 | } 6 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/InjectedDependency.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import jakarta.inject.Singleton; 4 | 5 | @Singleton 6 | public class InjectedDependency { 7 | } 8 | -------------------------------------------------------------------------------- /core/runtime/src/main/java-templates/io/quarkiverse/operatorsdk/runtime/Versions.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | public final class Versions { 4 | 5 | public static final String QUARKUS = "${quarkus.version}"; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /core/deployment/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.http.test-port=0 2 | quarkus.operator-sdk.generate-with-watched-namespaces=operator-level-buildtime-ns 3 | quarkus.operator-sdk.controllers.simple.generate-with-watched-namespaces=simple-ns -------------------------------------------------------------------------------- /samples/external-crd/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # only reference external CRDs for tests here 2 | %test.quarkus.operator-sdk.crd.external-crd-locations=external-crds/external.crd.yml 3 | quarkus.kubernetes-client.devservices.override-kubeconfig=true 4 | -------------------------------------------------------------------------------- /samples/pingpong/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.operator-sdk.crd.apply=true 2 | quarkus.container-image.builder=jib 3 | quarkus.operator-sdk.bundle.channels=alpha 4 | quarkus.kubernetes-client.devservices.override-kubeconfig=true 5 | quarkus.http.test-port=0 6 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/helm/static/values.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-07/schema#", 3 | "title": "Values", 4 | "type": "object", 5 | "properties": { 6 | "watchNamespaces": { 7 | "type": "string" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/config.adoc: -------------------------------------------------------------------------------- 1 | = Configuration reference 2 | 3 | == General configuration reference 4 | 5 | include::includes/quarkus-operator-sdk.adoc[] 6 | 7 | == OLM bundle generator configuration reference 8 | 9 | include::includes/quarkus-operator-sdk-bundle-generator.adoc[] 10 | 11 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/GeneratedBundleBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.deployment; 2 | 3 | import io.quarkus.builder.item.SimpleBuildItem; 4 | 5 | public final class GeneratedBundleBuildItem extends SimpleBuildItem { 6 | } 7 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/helm/static/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/managed-by: quarkus 6 | app.kubernetes.io/name: {{ .Chart.Name }} 7 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 8 | name: {{ .Chart.Name }} 9 | -------------------------------------------------------------------------------- /integration-tests/basic/src/test/java/io/quarkiverse/operatorsdk/it/NativeOperatorSDKResourceIT.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.quarkus.test.junit.QuarkusIntegrationTest; 4 | 5 | @QuarkusIntegrationTest 6 | public class NativeOperatorSDKResourceIT extends OperatorSDKResourceTest { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/CustomRateConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ ElementType.TYPE }) 7 | public @interface CustomRateConfiguration { 8 | 9 | int value(); 10 | } 11 | -------------------------------------------------------------------------------- /docs/modules/ROOT/nav.adoc: -------------------------------------------------------------------------------- 1 | * xref:index.adoc[Quarkus Operator SDK (QOSDK)] 2 | * xref:deploy-with-kubernetes-deployment.adoc[Deploying with a Kubernetes Deployment] 3 | * xref:deploy-with-olm.adoc[Deploying with OLM] 4 | * xref:deploy-with-helm.adoc[Deploying with Helm] 5 | * xref:upgrade.adoc[Upgrading] 6 | * xref:config.adoc[Configuration Reference] 7 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/Test.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.model.annotation.Group; 4 | import io.fabric8.kubernetes.model.annotation.Version; 5 | 6 | @Group("josdk.quarkiverse.io") 7 | @Version("v1alpha1") 8 | public class Test extends TestResource { 9 | } 10 | -------------------------------------------------------------------------------- /samples/joke/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | joke-api/mp-rest/url=https://v2.jokeapi.dev/joke 2 | quarkus.operator-sdk.crd.apply=true 3 | quarkus.container-image.builder=jib 4 | quarkus.operator-sdk.bundle.channels=alpha 5 | quarkus.operator-sdk.crd.generate-all=true 6 | quarkus.kubernetes-client.devservices.override-kubeconfig=true 7 | quarkus.http.test-port=0 8 | -------------------------------------------------------------------------------- /cli/src/main/java/io/quarkiverse/operatorsdk/cli/QOSDKCommand.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.cli; 2 | 3 | import io.quarkus.picocli.runtime.annotations.TopCommand; 4 | import picocli.CommandLine; 5 | 6 | @TopCommand 7 | @CommandLine.Command(name = "qosdk", header = "Java Operator SDK commands", subcommands = API.class) 8 | public class QOSDKCommand { 9 | } 10 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ChildTestResource2.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.model.annotation.Group; 4 | import io.fabric8.kubernetes.model.annotation.Version; 5 | 6 | @Group("example.com") 7 | @Version("v2") 8 | public class ChildTestResource2 extends TestResource { 9 | } 10 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ChildTestResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.model.annotation.Group; 4 | import io.fabric8.kubernetes.model.annotation.Version; 5 | 6 | @Group("example.com") 7 | @Version("v1") 8 | public class ChildTestResource extends TestResource { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /samples/mysql-schema/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.container-image.builder=jib 2 | quarkus.native.additional-build-args=--initialize-at-run-time=org.apache.commons.lang3.RandomStringUtils 3 | 4 | quarkus.datasource.username=root 5 | quarkus.datasource.devservices.image-name=mariadb:10.7 6 | quarkus.kubernetes-client.devservices.override-kubeconfig=true 7 | quarkus.http.test-port=0 8 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | name: Quarkiverse Pre Release 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '.github/project.yml' 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | pre-release: 14 | name: Pre-Release 15 | uses: quarkiverse/.github/.github/workflows/pre-release.yml@main 16 | secrets: inherit -------------------------------------------------------------------------------- /.github/workflows/release-snapshot-on-push-selected-branches.yml: -------------------------------------------------------------------------------- 1 | name: Release snapshots of selected branches on push 2 | 3 | on: 4 | push: 5 | branches: [ main, next, next-fabric8-version ] 6 | 7 | defaults: 8 | run: 9 | shell: bash 10 | 11 | jobs: 12 | release-snapshot: 13 | uses: ./.github/workflows/release-snapshot.yml 14 | secrets: inherit 15 | with: 16 | branch: "${{github.ref}}" 17 | -------------------------------------------------------------------------------- /samples/mysql-schema/src/main/java/io/quarkiverse/operatorsdk/samples/mysqlschema/SchemaSpec.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.mysqlschema; 2 | 3 | public class SchemaSpec { 4 | 5 | private String encoding; 6 | 7 | public String getEncoding() { 8 | return encoding; 9 | } 10 | 11 | public void setEncoding(String encoding) { 12 | this.encoding = encoding; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/Keycloak.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Version; 6 | 7 | @Group("org.keycloak") 8 | @Version("v1alpha2") 9 | public class Keycloak extends CustomResource { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/TestUUIDBean.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import java.util.UUID; 4 | 5 | import jakarta.enterprise.context.ApplicationScoped; 6 | 7 | @ApplicationScoped 8 | public class TestUUIDBean { 9 | 10 | private final String id = UUID.randomUUID().toString(); 11 | 12 | public String uuid() { 13 | return id; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/exposedapp/src/test/java/io/halkyon/NativeExposedAppReconcilerIT.java: -------------------------------------------------------------------------------- 1 | package io.halkyon; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | 5 | import io.quarkus.test.junit.QuarkusIntegrationTest; 6 | 7 | @QuarkusIntegrationTest 8 | @Disabled("Currently not possible to inject the dev service-provided k8s client in native app") 9 | public class NativeExposedAppReconcilerIT extends ExposedAppReconcilerTest { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/TestCR.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Version; 6 | 7 | @Group("josdk.quarkiverse.io") 8 | @Version("v1") 9 | public class TestCR extends CustomResource { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/TestResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Version; 6 | 7 | @Group("example.com") 8 | @Version("v1") 9 | public abstract class TestResource extends CustomResource { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/helm/static/README.md: -------------------------------------------------------------------------------- 1 | ## Configuration 2 | 3 | The following values are configurable: 4 | 5 | - `watchNamespaces` - namespaces to be watched, either: 6 | - a list of comma-separated namespace names 7 | - `JOSDK_ALL_NAMESPACES` to watch all namespaces 8 | - `JOSDK_WATCH_CURRENT` to watch only the namespace in which the operator is deployed 9 | - `version` - the current version of the application. 10 | 11 | -------------------------------------------------------------------------------- /common/src/main/resources/templates/cli/api/resource.qute: -------------------------------------------------------------------------------- 1 | package {packageName}; 2 | 3 | import io.fabric8.kubernetes.api.model.Namespaced; 4 | import io.fabric8.kubernetes.client.CustomResource; 5 | import io.fabric8.kubernetes.model.annotation.Group; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Version("{version}") 9 | @Group("{group}") 10 | public class {kind} extends CustomResource<{kind}Spec, {kind}Status> implements Namespaced { } -------------------------------------------------------------------------------- /bundle-generator/runtime/src/main/resources/META-INF/quarkus-extension.yaml: -------------------------------------------------------------------------------- 1 | name: Operator SDK - OLM Bundle Generator 2 | description: OLM Bundle Generation for Java Operator SDK operators 3 | metadata: 4 | keywords: 5 | - operator 6 | - kubernetes 7 | - openshift 8 | - olm 9 | - csv 10 | - bundle 11 | categories: 12 | - cloud 13 | short-name: "olm" 14 | status: "stable" 15 | config: 16 | - "quarkus.operator-sdk.bundle." -------------------------------------------------------------------------------- /samples/mysql-schema/src/main/java/io/quarkiverse/operatorsdk/samples/mysqlschema/dependent/ResourcePollerConfig.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.mysqlschema.dependent; 2 | 3 | public class ResourcePollerConfig { 4 | private final int pollPeriod; 5 | 6 | public ResourcePollerConfig(int pollPeriod) { 7 | this.pollPeriod = pollPeriod; 8 | } 9 | 10 | public int getPollPeriod() { 11 | return pollPeriod; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ExternalDependentResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; 4 | 5 | public class ExternalDependentResource extends KubernetesDependentResource { 6 | 7 | public ExternalDependentResource() { 8 | super(External.class); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ADRConfigurationAnnotation.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface ADRConfigurationAnnotation { 11 | int value(); 12 | } 13 | -------------------------------------------------------------------------------- /annotations/src/main/java/io/quarkiverse/operatorsdk/annotations/SharedCSVMetadata.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.annotations; 2 | 3 | /** 4 | * A marker interface used to identify classes bearing {@link CSVMetadata} that needs to be shared across reconcilers using the 5 | * same {@link CSVMetadata#bundleName()} attribute. Note that sharing metadata without using {@link SharedCSVMetadata} is not 6 | * allowed. 7 | */ 8 | public interface SharedCSVMetadata { 9 | } 10 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/CRUDConfigMap.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.ConfigMap; 4 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; 5 | 6 | public class CRUDConfigMap extends CRUDKubernetesDependentResource { 7 | 8 | public CRUDConfigMap() { 9 | super(ConfigMap.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/TestLeaderElectionConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import jakarta.inject.Singleton; 4 | 5 | import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; 6 | 7 | @Singleton 8 | public class TestLeaderElectionConfiguration extends LeaderElectionConfiguration { 9 | 10 | public TestLeaderElectionConfiguration() { 11 | super("testLeaseName"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/PodDependentResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.Pod; 4 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; 5 | 6 | public class PodDependentResource extends KubernetesDependentResource { 7 | 8 | public PodDependentResource() { 9 | super(Pod.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /annotations/src/main/java/io/quarkiverse/operatorsdk/annotations/AdditionalRBACRules.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ ElementType.TYPE }) 10 | @SuppressWarnings("unused") 11 | public @interface AdditionalRBACRules { 12 | RBACRule[] value(); 13 | } 14 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/NoOpMetricsProvider.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import jakarta.enterprise.inject.Produces; 4 | import jakarta.inject.Singleton; 5 | 6 | import io.javaoperatorsdk.operator.api.monitoring.Metrics; 7 | import io.quarkus.arc.DefaultBean; 8 | 9 | @Singleton 10 | public class NoOpMetricsProvider { 11 | @Produces 12 | @DefaultBean 13 | public Metrics getMetrics() { 14 | return Metrics.NOOP; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ADRConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.quarkus.runtime.annotations.RecordableConstructor; 4 | 5 | public class ADRConfiguration { 6 | private final int value; 7 | 8 | @RecordableConstructor 9 | public ADRConfiguration(int value) { 10 | this.value = value; 11 | } 12 | 13 | @SuppressWarnings("unused") 14 | public int getValue() { 15 | return value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /annotations/src/main/java/io/quarkiverse/operatorsdk/annotations/AdditionalRBACRoleRefs.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ ElementType.TYPE }) 10 | @SuppressWarnings("unused") 11 | public @interface AdditionalRBACRoleRefs { 12 | RBACRoleRef[] value(); 13 | } 14 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/TestResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.ShortNames; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group("josdk.quarkiverse.io") 9 | @Version("v1alpha1") 10 | @ShortNames("tr") 11 | public class TestResource extends CustomResource { 12 | } 13 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/Constants.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import java.util.Collections; 4 | import java.util.Set; 5 | 6 | public final class Constants { 7 | private Constants() { 8 | } 9 | 10 | public static final Set QOSDK_USE_BUILDTIME_NAMESPACES_SET = Collections 11 | .singleton(Constants.QOSDK_USE_BUILDTIME_NAMESPACES); 12 | 13 | public static final String QOSDK_USE_BUILDTIME_NAMESPACES = "QOSDK_USE_BUILDTIME_NAMESPACES"; 14 | } 15 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/VersionBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import io.quarkiverse.operatorsdk.runtime.Version; 4 | import io.quarkus.builder.item.SimpleBuildItem; 5 | 6 | public final class VersionBuildItem extends SimpleBuildItem { 7 | private final Version version; 8 | 9 | public VersionBuildItem(Version version) { 10 | this.version = version; 11 | } 12 | 13 | public Version getVersion() { 14 | return version; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/EmptyCR.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Version; 6 | import io.quarkiverse.operatorsdk.it.EmptyCR.EmptySpec; 7 | 8 | @Group("josdk.quarkiverse.io") 9 | @Version("v1alpha1") 10 | public class EmptyCR extends CustomResource { 11 | 12 | static class EmptySpec { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/OptionalExplicitNativeDependent.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.openshift.api.model.monitoring.v1.ServiceMonitor; 4 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; 5 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 6 | 7 | @CSVMetadata.Optional 8 | public class OptionalExplicitNativeDependent extends KubernetesDependentResource { 9 | } 10 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/helm/static/generic-crd-cluster-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: {{ .Chart.Name }}-crd-validating-cluster-role 5 | labels: 6 | app.kubernetes.io/name: {{ .Chart.Name }} 7 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 8 | app.kubernetes.io/managed-by: quarkus 9 | rules: 10 | - apiGroups: 11 | - apiextensions.k8s.io 12 | resources: 13 | - customresourcedefinitions 14 | verbs: 15 | - get 16 | - list 17 | -------------------------------------------------------------------------------- /samples/pingpong/src/main/java/io/quarkiverse/operatorsdk/samples/pingpong/PingPongOperatorCSVMetadata.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.pingpong; 2 | 3 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 4 | import io.quarkiverse.operatorsdk.annotations.SharedCSVMetadata; 5 | 6 | @CSVMetadata(bundleName = PingPongOperatorCSVMetadata.BUNDLE_NAME) 7 | @SuppressWarnings("unused") 8 | public class PingPongOperatorCSVMetadata implements SharedCSVMetadata { 9 | public static final String BUNDLE_NAME = "pingpong-operator"; 10 | } 11 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/UnownedCRDInfoBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import io.quarkiverse.operatorsdk.runtime.CRDInfos; 4 | import io.quarkus.builder.item.SimpleBuildItem; 5 | 6 | public final class UnownedCRDInfoBuildItem extends SimpleBuildItem { 7 | private final CRDInfos crds; 8 | 9 | public UnownedCRDInfoBuildItem(CRDInfos crds) { 10 | this.crds = crds; 11 | } 12 | 13 | public CRDInfos getCRDs() { 14 | return crds; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /integration-tests/basic/src/test/java/io/quarkiverse/operatorsdk/it/EmptyOperatorLevelNamespacesTestProfile.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | import io.quarkus.test.junit.QuarkusTestProfile; 7 | 8 | public class EmptyOperatorLevelNamespacesTestProfile implements QuarkusTestProfile { 9 | 10 | @Override 11 | public Map getConfigOverrides() { 12 | return Collections.singletonMap("quarkus.operator-sdk.namespaces", ""); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /samples/mysql-schema/src/main/java/io/quarkiverse/operatorsdk/samples/mysqlschema/MySQLSchema.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.mysqlschema; 2 | 3 | import io.fabric8.kubernetes.api.model.Namespaced; 4 | import io.fabric8.kubernetes.client.CustomResource; 5 | import io.fabric8.kubernetes.model.annotation.Group; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group("mysql.sample.javaoperatorsdk") 9 | @Version("v1") 10 | public class MySQLSchema extends CustomResource implements Namespaced { 11 | } 12 | -------------------------------------------------------------------------------- /core/runtime/src/main/resources/META-INF/quarkus-extension.yaml: -------------------------------------------------------------------------------- 1 | name: Operator SDK 2 | description: Quarkus extension for the Java Operator SDK (https://javaoperatorsdk.io) 3 | metadata: 4 | keywords: 5 | - operator 6 | - kubernetes 7 | - openshift 8 | categories: 9 | - "cloud" 10 | short-name: "qosdk" 11 | status: "stable" 12 | guide: "https://docs.quarkiverse.io/quarkus-operator-sdk/dev/index.html" 13 | config: 14 | - "quarkus.operator-sdk." 15 | cli-plugins: 16 | - "${project.groupId}:quarkus-operator-sdk-cli:${project.version}" 17 | -------------------------------------------------------------------------------- /integration-tests/basic/src/test/java/io/quarkiverse/operatorsdk/it/SetOperatorLevelNamespacesTestProfile.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | import io.quarkus.test.junit.QuarkusTestProfile; 7 | 8 | public class SetOperatorLevelNamespacesTestProfile implements QuarkusTestProfile { 9 | 10 | @Override 11 | public Map getConfigOverrides() { 12 | return Collections.singletonMap("quarkus.operator-sdk.namespaces", "operator-level"); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/CRDGenerator.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import io.fabric8.kubernetes.client.CustomResource; 8 | import io.quarkiverse.operatorsdk.runtime.CRDInfos; 9 | 10 | interface CRDGenerator { 11 | void generate(List crdSpecVersions, File outputDir, Set generated, CRDInfos converted); 12 | 13 | void scheduleForGeneration(Class> crClass); 14 | } 15 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/helm/static/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ .Chart.Name }} 6 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 7 | app.kubernetes.io/managed-by: quarkus 8 | name: {{ .Chart.Name }} 9 | spec: 10 | ports: 11 | - name: http 12 | port: 80 13 | protocol: TCP 14 | targetPort: 8080 15 | selector: 16 | app.kubernetes.io/name: {{ .Chart.Name }} 17 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 18 | type: ClusterIP 19 | -------------------------------------------------------------------------------- /.github/workflows/release-snapshot-nightly-next-fabric8-version.yml: -------------------------------------------------------------------------------- 1 | name: Release snapshot of next-fabric8-version branch each day at midnight 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | build-for-latest-quarkus-version: 9 | uses: ./.github/workflows/build-for-quarkus-version.yml 10 | with: 11 | quarkus-pr: "42335" 12 | java-version: 21 13 | native-modules: "integration-tests,samples" 14 | profiles: "use-snapshots,override-fkc" 15 | mvnArgs: "-Dfkc.version=7.0-SNAPSHOT -Ddefault=false" 16 | branch: "main" -------------------------------------------------------------------------------- /common/src/main/resources/templates/cli/api/reconciler.qute: -------------------------------------------------------------------------------- 1 | package {packageName}; 2 | 3 | import io.fabric8.kubernetes.client.KubernetesClient; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | 8 | public class {kind}Reconciler implements Reconciler<{kind}> { 9 | @Override 10 | public UpdateControl<{kind}> reconcile({kind} resource, Context<{kind}> context) { 11 | return UpdateControl.noUpdate(); 12 | } 13 | } -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/ExternalV1.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Kind; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group("halkyon.io") 9 | @Version(ExternalV1.VERSION) 10 | @Kind(External.KIND) 11 | public class ExternalV1 extends CustomResource { 12 | public static final String VERSION = "v1"; 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/release-prepare.yml: -------------------------------------------------------------------------------- 1 | name: Quarkiverse Prepare Release 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: [ closed ] 7 | paths: 8 | - '.github/project.yml' 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | prepare-release: 16 | name: Prepare Release 17 | if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true}} 18 | uses: quarkiverse/.github/.github/workflows/prepare-release.yml@main 19 | secrets: inherit 20 | -------------------------------------------------------------------------------- /integration-tests/basic/src/test/java/io/quarkiverse/operatorsdk/it/CustomKubernetesServerTestResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.quarkus.test.kubernetes.client.KubernetesServerTestResource; 4 | 5 | public class CustomKubernetesServerTestResource extends KubernetesServerTestResource { 6 | 7 | @Override 8 | protected void configureServer() { 9 | super.configureServer(); 10 | 11 | server.expect().get().withPath("/version") 12 | .andReturn(200, "{\"major\": \"13\", \"minor\": \"37\"}").always(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/SimpleCRV2.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Kind; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group("example.com") 9 | @Version(SimpleCRV2.VERSION) 10 | @Kind(SimpleCR.KIND) 11 | public class SimpleCRV2 extends CustomResource { 12 | public static final String VERSION = "v2"; 13 | } 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "maven" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/upgrade.adoc: -------------------------------------------------------------------------------- 1 | = Upgrading from 6.x to 7.0 2 | 3 | == Removed deprecated code 4 | 5 | - `KubernetesClientSerializationCustomer` has been removed, use `io.quarkus.kubernetes.client.KubernetesClientObjectMapperCustomizer` from Quarkus instead 6 | - `BundleGenerationConfiguration.packageName` and associated property (`quarkus.operator-sdk.bundle.package-name`) have been removed, use `CSVMetadata.bundleName` instead 7 | - `SharedCSVMetadata` and `CSVMetadata` from `quarkus-operator-sdk-bundle-generator` annotations that got moved to `quarkus-operator-sdk-annotations` module for better reuse 8 | 9 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ReconcilerWithNoCsvMetadata.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 5 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 6 | 7 | public class ReconcilerWithNoCsvMetadata implements Reconciler { 8 | 9 | @Override 10 | public UpdateControl reconcile(First request, Context context) { 11 | return UpdateControl.noUpdate(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/SimpleReconcilerV2.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 5 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 6 | 7 | public class SimpleReconcilerV2 implements Reconciler { 8 | @Override 9 | public UpdateControl reconcile(SimpleCRV2 simpleCRV2, Context context) throws Exception { 10 | return UpdateControl.noUpdate(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/GeneratedCRDInfoBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import io.quarkiverse.operatorsdk.runtime.CRDGenerationInfo; 4 | import io.quarkus.builder.item.SimpleBuildItem; 5 | 6 | public final class GeneratedCRDInfoBuildItem extends SimpleBuildItem { 7 | private final CRDGenerationInfo crdInfo; 8 | 9 | public GeneratedCRDInfoBuildItem(CRDGenerationInfo crdInfo) { 10 | this.crdInfo = crdInfo; 11 | } 12 | 13 | public CRDGenerationInfo getCRDGenerationInfo() { 14 | return crdInfo; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/GatewayReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.gatewayapi.v1.Gateway; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | 8 | public class GatewayReconciler implements Reconciler { 9 | 10 | @Override 11 | public UpdateControl reconcile(Gateway gateway, Context context) { 12 | return UpdateControl.noUpdate(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/pingpong/src/main/java/io/quarkiverse/operatorsdk/samples/pingpong/Status.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.pingpong; 2 | 3 | public class Status { 4 | public enum State { 5 | PROCESSED, 6 | UNKNOWN 7 | } 8 | 9 | private Status.State state = Status.State.UNKNOWN; 10 | 11 | public Status() { 12 | 13 | } 14 | 15 | public Status(Status.State state) { 16 | this.state = state; 17 | } 18 | 19 | public State getState() { 20 | return state; 21 | } 22 | 23 | public void setState(State state) { 24 | this.state = state; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/scripts/testJokeSample.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | NAMESPACE="${1}" 3 | 4 | # Ensure the CRD is installed 5 | if ! .github/scripts/waitFor.sh crd $NAMESPACE jokerequests.samples.javaoperatorsdk.io; then 6 | echo "CRD not installed: cannot run the Joke operator" 7 | exit 1; 8 | fi 9 | 10 | # Test operator by creating a Joke Request resource 11 | kubectl apply -f samples/joke/src/main/k8s/jokerequest.yml 12 | 13 | # And wait for the operator to create another Joke resource 14 | if ! .github/scripts/waitFor.sh joke $NAMESPACE NAME; then 15 | echo "Joke was not properly created in response to request" 16 | exit 1; 17 | fi 18 | 19 | exit 0; -------------------------------------------------------------------------------- /common-deployment/src/test/java/io/quarkiverse/operatorsdk/common/ConfigurationUtilsTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.common; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import io.javaoperatorsdk.operator.api.config.Utils; 7 | 8 | class ConfigurationUtilsTest { 9 | @Test 10 | void shouldUseSystemPropertyIfPresent() { 11 | if (Utils.isValidateCustomResourcesEnvVarSet()) { 12 | Assertions.assertEquals(Utils.shouldCheckCRDAndValidateLocalModel(), 13 | ConfigurationUtils.shouldValidateCustomResources(false)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/helm/static/generic-crd-cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: {{ .Chart.Name }}-crd-validating-role-binding 5 | labels: 6 | app.kubernetes.io/name: {{ .Chart.Name }} 7 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 8 | app.kubernetes.io/managed-by: quarkus 9 | roleRef: 10 | kind: ClusterRole 11 | apiGroup: rbac.authorization.k8s.io 12 | name: {{ .Chart.Name }}-crd-validating-cluster-role 13 | subjects: 14 | - kind: ServiceAccount 15 | name: {{ .Chart.Name }} 16 | namespace: {{ .Release.Namespace }} 17 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/External.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Kind; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group("halkyon.io") 9 | @Version(value = External.VERSION, storage = false) 10 | @Kind(External.KIND) 11 | public class External extends CustomResource { 12 | public static final String VERSION = "v1alpha1"; 13 | public static final String KIND = "External"; 14 | } 15 | -------------------------------------------------------------------------------- /annotations/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.quarkiverse.operatorsdk 6 | quarkus-operator-sdk-parent 7 | 7.4.1-SNAPSHOT 8 | 9 | 10 | quarkus-operator-sdk-annotations 11 | Quarkus - Operator SDK - Annotations 12 | 13 | 14 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ShouldBeIgnoredReconciler2.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.HasMetadata; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | 8 | public class ShouldBeIgnoredReconciler2 implements Reconciler { 9 | 10 | @Override 11 | public UpdateControl reconcile(HasMetadata hasMetadata, Context context) { 12 | return UpdateControl.noUpdate(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/SimpleCR.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Kind; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group("example.com") 9 | @Version(value = SimpleCR.VERSION, storage = false) 10 | @Kind(SimpleCR.KIND) 11 | public class SimpleCR extends CustomResource { 12 | 13 | public static final String VERSION = "v1"; 14 | public static final String KIND = "SimpleCR"; 15 | } 16 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # More details are here: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 5 | 6 | # The '*' pattern is global owners. 7 | 8 | # Order is important. The last matching pattern has the most precedence. 9 | # The folders are ordered as follows: 10 | 11 | # In each subsection folders are ordered first by depth, then alphabetically. 12 | # This should make it easy to add new rules without breaking existing ones. 13 | 14 | * @quarkiverse/quarkiverse-operator-sdk 15 | -------------------------------------------------------------------------------- /samples/external-crd/external-crds/external.crd.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: externals.halkyon.io 5 | spec: 6 | conversion: 7 | strategy: None 8 | group: halkyon.io 9 | names: 10 | kind: External 11 | listKind: ExternalList 12 | plural: externals 13 | singular: external 14 | scope: Namespaced 15 | versions: 16 | - name: v1 17 | schema: 18 | openAPIV3Schema: 19 | properties: 20 | spec: 21 | type: object 22 | status: 23 | type: object 24 | type: object 25 | served: true 26 | storage: true -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/First.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | 5 | import io.fabric8.kubernetes.api.model.Namespaced; 6 | import io.fabric8.kubernetes.client.CustomResource; 7 | import io.fabric8.kubernetes.model.annotation.Group; 8 | import io.fabric8.kubernetes.model.annotation.Version; 9 | 10 | @Group("samples.javaoperatorsdk.io") 11 | @Version("v1alpha1") 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class First extends CustomResource implements Namespaced { 14 | 15 | public First() { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/Second.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | 5 | import io.fabric8.kubernetes.api.model.Namespaced; 6 | import io.fabric8.kubernetes.client.CustomResource; 7 | import io.fabric8.kubernetes.model.annotation.Group; 8 | import io.fabric8.kubernetes.model.annotation.Version; 9 | 10 | @Group("samples.javaoperatorsdk.io") 11 | @Version("v1alpha1") 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class Second extends CustomResource implements Namespaced { 14 | 15 | public Second() { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/IgnoredByAnnotationReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.Ignore; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | 8 | @Ignore 9 | public class IgnoredByAnnotationReconciler implements Reconciler { 10 | 11 | @Override 12 | public UpdateControl reconcile(TestResource testResource, Context context) { 13 | return UpdateControl.noUpdate(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/KubernetesClientObjectMapperCustomizer.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import jakarta.inject.Singleton; 4 | 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.SerializationFeature; 7 | 8 | @Singleton 9 | public class KubernetesClientObjectMapperCustomizer implements 10 | io.quarkus.kubernetes.client.KubernetesClientObjectMapperCustomizer { 11 | 12 | @Override 13 | public void customize(ObjectMapper mapper) { 14 | // disable failure on empty beans 15 | mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/external-crds/external.crd.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: externals.halkyon.io 5 | spec: 6 | conversion: 7 | strategy: None 8 | group: halkyon.io 9 | names: 10 | kind: External 11 | listKind: ExternalList 12 | plural: externals 13 | singular: external 14 | scope: Namespaced 15 | versions: 16 | - name: v1 17 | schema: 18 | openAPIV3Schema: 19 | properties: 20 | spec: 21 | type: object 22 | status: 23 | type: object 24 | type: object 25 | served: true 26 | storage: true -------------------------------------------------------------------------------- /helm/deployment/src/test/java/io/quarkiverse/operatorsdk/helm/deployment/test/sources/SimpleCR.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.helm.deployment.test.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Kind; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group("example.com") 9 | @Version(value = SimpleCR.VERSION, storage = false) 10 | @Kind(SimpleCR.KIND) 11 | public class SimpleCR extends CustomResource { 12 | 13 | public static final String VERSION = "v1"; 14 | public static final String KIND = "SimpleCR"; 15 | } 16 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/TestMetrics.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import jakarta.inject.Singleton; 4 | 5 | import io.javaoperatorsdk.operator.api.monitoring.Metrics; 6 | import io.micrometer.core.instrument.MeterRegistry; 7 | import io.micrometer.core.instrument.binder.MeterBinder; 8 | 9 | @Singleton 10 | public class TestMetrics implements Metrics, MeterBinder { 11 | private boolean registryBound; 12 | 13 | @Override 14 | public void bindTo(MeterRegistry meterRegistry) { 15 | registryBound = true; 16 | } 17 | 18 | public boolean isRegistryBound() { 19 | return registryBound; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/CReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.ServiceAccount; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | 8 | public class CReconciler implements Reconciler { 9 | 10 | @Override 11 | public UpdateControl reconcile(ServiceAccount serviceAccount, 12 | Context context) throws Exception { 13 | return null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/BuildTimeConfigurationServiceBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import io.quarkiverse.operatorsdk.runtime.BuildTimeConfigurationService; 4 | import io.quarkus.builder.item.SimpleBuildItem; 5 | 6 | public final class BuildTimeConfigurationServiceBuildItem extends SimpleBuildItem { 7 | private final BuildTimeConfigurationService service; 8 | 9 | BuildTimeConfigurationServiceBuildItem(BuildTimeConfigurationService service) { 10 | this.service = service; 11 | } 12 | 13 | public BuildTimeConfigurationService getConfigurationService() { 14 | return service; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/NonKubeResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 5 | import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; 6 | 7 | public class NonKubeResource implements DependentResource { 8 | 9 | @Override 10 | public ReconcileResult reconcile(TestCR testCR, Context context) { 11 | return null; 12 | } 13 | 14 | @Override 15 | public Class resourceType() { 16 | return Foo.class; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/exposedapp/src/main/java/io/halkyon/ExposedApp.java: -------------------------------------------------------------------------------- 1 | package io.halkyon; 2 | 3 | import io.fabric8.kubernetes.api.model.Namespaced; 4 | import io.fabric8.kubernetes.client.CustomResource; 5 | import io.fabric8.kubernetes.model.annotation.Group; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Version("v1alpha1") 9 | @Group("halkyon.io") 10 | public class ExposedApp extends CustomResource implements Namespaced { 11 | 12 | @Override 13 | protected ExposedAppSpec initSpec() { 14 | return new ExposedAppSpec(); 15 | } 16 | 17 | @Override 18 | protected ExposedAppStatus initStatus() { 19 | return new ExposedAppStatus(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /annotations/src/main/java/io/quarkiverse/operatorsdk/annotations/RBACRule.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ ElementType.TYPE }) 7 | @Repeatable(AdditionalRBACRules.class) 8 | @SuppressWarnings("unused") 9 | public @interface RBACRule { 10 | /** 11 | * Represents a wildcard string that matches any RBAC-related value (verb, resource, etc…). 12 | */ 13 | String ALL = "*"; 14 | 15 | String[] apiGroups() default {}; 16 | 17 | String[] verbs(); 18 | 19 | String[] resources() default {}; 20 | 21 | String[] resourceNames() default {}; 22 | 23 | String[] nonResourceURLs() default {}; 24 | } 25 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/LabelAdderCRDPostProcessor.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.crdv2.generator.CRDPostProcessor; 4 | import io.fabric8.kubernetes.api.model.HasMetadata; 5 | 6 | public class LabelAdderCRDPostProcessor implements CRDPostProcessor { 7 | 8 | public static final String LABEL_NAME = "foo"; 9 | public static final String LABEL_VALUE = "bar"; 10 | 11 | @Override 12 | public HasMetadata process(HasMetadata crd, String crdSpecVersion) { 13 | final var meta = crd.getMetadata().edit().addToLabels(LABEL_NAME, LABEL_VALUE).build(); 14 | crd.setMetadata(meta); 15 | return crd; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/NoDefaultArgConstructorDependent.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import jakarta.inject.Singleton; 4 | 5 | import io.fabric8.kubernetes.api.model.Secret; 6 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; 7 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; 8 | 9 | @KubernetesDependent 10 | @Singleton 11 | public class NoDefaultArgConstructorDependent extends KubernetesDependentResource { 12 | 13 | public NoDefaultArgConstructorDependent(InjectedDependency injectedDependency) { 14 | super(Secret.class); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/SecondExternal.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Kind; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group(SecondExternal.GROUP) 9 | @Version(SecondExternal.VERSION) 10 | @Kind(SecondExternal.KIND) 11 | public class SecondExternal extends CustomResource { 12 | 13 | public static final String GROUP = "halkyon.io"; 14 | public static final String VERSION = "v1alpha1"; 15 | public static final String KIND = "ExternalAgain"; 16 | } 17 | -------------------------------------------------------------------------------- /core/runtime/src/test/resources/external.crd.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: externals.halkyon.io 5 | spec: 6 | conversion: 7 | strategy: None 8 | group: halkyon.io 9 | names: 10 | kind: External 11 | listKind: ExternalList 12 | plural: externals 13 | singular: external 14 | scope: Namespaced 15 | versions: 16 | - name: v1 17 | schema: 18 | openAPIV3Schema: 19 | properties: 20 | spec: 21 | type: object 22 | status: 23 | type: object 24 | type: object 25 | served: true 26 | storage: true 27 | selectableFields: 28 | - jsonPath: "spec" 29 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ShouldBeIgnoredReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | 8 | public class ShouldBeIgnoredReconciler implements Reconciler> { 9 | 10 | @Override 11 | public UpdateControl> reconcile(CustomResource resource, 12 | Context> context) { 13 | return UpdateControl.noUpdate(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/external-crds/v1beta1spec.crd.yml: -------------------------------------------------------------------------------- 1 | # Generated by Fabric8 CRDGenerator, manual edits might get overwritten! 2 | apiVersion: apiextensions.k8s.io/v1beta1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: v1beta1s.test.com 6 | spec: 7 | group: test.com 8 | names: 9 | kind: V1Beta1 10 | plural: v1beta1s 11 | singular: v1beta1 12 | scope: Namespaced 13 | subresources: 14 | status: { } 15 | validation: 16 | openAPIV3Schema: 17 | properties: 18 | spec: 19 | properties: 20 | value: 21 | type: string 22 | type: object 23 | type: object 24 | versions: 25 | - name: v1 26 | served: true 27 | storage: true -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/ReconcilerInfosBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import java.util.Map; 4 | 5 | import io.quarkiverse.operatorsdk.common.ReconcilerAugmentedClassInfo; 6 | import io.quarkus.builder.item.SimpleBuildItem; 7 | 8 | public final class ReconcilerInfosBuildItem extends SimpleBuildItem { 9 | private final Map reconcilers; 10 | 11 | public ReconcilerInfosBuildItem(Map reconcilers) { 12 | this.reconcilers = reconcilers; 13 | } 14 | 15 | public Map getReconcilers() { 16 | return reconcilers; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/External.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Version; 6 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 7 | 8 | @Group("halkyon.io") 9 | @Version("v1alpha1") 10 | @CSVMetadata(displayName = External.DISPLAY_NAME, description = External.DESCRIPTION) 11 | public class External extends CustomResource { 12 | 13 | public static final String DISPLAY_NAME = "External display name"; 14 | public static final String DESCRIPTION = "External description"; 15 | } 16 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/BReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.Service; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 8 | 9 | @CSVMetadata(bundleName = AReconciler.SHARED, version = "0.0.2") 10 | public class BReconciler implements Reconciler { 11 | 12 | @Override 13 | public UpdateControl reconcile(Service service, Context context) { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /annotations/src/main/java/io/quarkiverse/operatorsdk/annotations/RBACRoleRef.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Repeatable; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Target({ ElementType.TYPE }) 11 | @Repeatable(AdditionalRBACRoleRefs.class) 12 | public @interface RBACRoleRef { 13 | String RBAC_API_GROUP = "rbac.authorization.k8s.io"; 14 | 15 | RoleKind kind() default RoleKind.Role; 16 | 17 | String name() default ""; 18 | 19 | enum RoleKind { 20 | 21 | ClusterRole, 22 | Role 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/ReadOnlySecret.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.Secret; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 6 | import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; 7 | 8 | public class ReadOnlySecret implements DependentResource { 9 | 10 | @Override 11 | public ReconcileResult reconcile(TestCR testCR, Context context) { 12 | return null; 13 | } 14 | 15 | @Override 16 | public Class resourceType() { 17 | return Secret.class; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/DuplicatedBundleNameWithoutSharedCSVMetadata1.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.openshift.api.model.Role; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 8 | 9 | @CSVMetadata(bundleName = "illegal") 10 | public class DuplicatedBundleNameWithoutSharedCSVMetadata1 implements Reconciler { 11 | 12 | @Override 13 | public UpdateControl reconcile(Role role, Context context) { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/UnownedReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | 8 | @ControllerConfiguration(name = UnownedReconciler.NAME) 9 | public class UnownedReconciler implements Reconciler { 10 | 11 | public static final String NAME = "unowned"; 12 | 13 | @Override 14 | public UpdateControl reconcile(TestCR testCR, Context context) throws Exception { 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/TypelessKubeResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.processing.GroupVersionKind; 4 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesDependentResource; 5 | 6 | public class TypelessKubeResource extends GenericKubernetesDependentResource { 7 | 8 | public static final String GROUP = "crd.josdk.quarkiverse.io"; 9 | public static final String KIND = "typeless"; 10 | public static final String VERSION = "v1"; 11 | private static final GroupVersionKind GVK = new GroupVersionKind(GROUP, VERSION, KIND); 12 | 13 | public TypelessKubeResource() { 14 | super(GVK); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /samples/external-crd/src/main/java/io/quarkiverse/operatorsdk/ExternalCRDReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk; 2 | 3 | import io.fabric8.kubernetes.api.model.Pod; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 6 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 7 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 8 | 9 | @ControllerConfiguration(name = ExternalCRDReconciler.NAME) 10 | public class ExternalCRDReconciler implements Reconciler { 11 | 12 | public static final String NAME = "externalcrd"; 13 | 14 | @Override 15 | public UpdateControl reconcile(Pod pod, Context context) { 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/scripts/testPingPongSample.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | NAMESPACE="${1}" 3 | 4 | # Ensure the CRD is installed 5 | if ! .github/scripts/waitFor.sh crd $NAMESPACE pings.samples.javaoperatorsdk.io; then 6 | exit 1; 7 | fi 8 | 9 | # Test operator by creating a Joke Request resource 10 | kubectl apply -f samples/pingpong/src/main/k8s/pingrequest.yml 11 | 12 | # The ping reconciler should mark the ping resource as processed 13 | if ! .github/scripts/waitFor.sh ping $NAMESPACE PROCESSED "my-ping-request -o jsonpath='{.status.state}'"; then 14 | exit 1; 15 | fi 16 | 17 | # And the pong reconciler should mark the new pong resource as processed 18 | if ! .github/scripts/waitFor.sh pong $NAMESPACE PROCESSED "my-ping-request-pong -o jsonpath='{.status.state}'"; then 19 | exit 1; 20 | fi 21 | 22 | exit 0; -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/Third.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.Namespaced; 4 | import io.fabric8.kubernetes.client.CustomResource; 5 | import io.fabric8.kubernetes.model.annotation.Group; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 8 | 9 | @Group("samples.javaoperatorsdk.io") 10 | @Version("v1alpha1") 11 | @CSVMetadata(displayName = Third.DISPLAY, description = Third.DESCRIPTION) 12 | public class Third extends CustomResource implements Namespaced { 13 | 14 | public static final String DESCRIPTION = "Third description"; 15 | public static final String DISPLAY = "Third display"; 16 | } 17 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/TypelessAnotherKubeResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import static io.quarkiverse.operatorsdk.test.sources.TypelessKubeResource.*; 4 | 5 | import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; 6 | import io.javaoperatorsdk.operator.processing.GroupVersionKind; 7 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesDependentResource; 8 | 9 | public class TypelessAnotherKubeResource extends GenericKubernetesDependentResource implements Deleter { 10 | 11 | private static final GroupVersionKind GVK = new GroupVersionKind(GROUP, VERSION, KIND); 12 | 13 | public TypelessAnotherKubeResource() { 14 | super(GVK); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/DuplicatedBundleNameWithoutSharedCSVMetadata2.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.openshift.api.model.RoleBinding; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 8 | 9 | @CSVMetadata(bundleName = "illegal") 10 | public class DuplicatedBundleNameWithoutSharedCSVMetadata2 implements Reconciler { 11 | 12 | @Override 13 | public UpdateControl reconcile(RoleBinding roleBinding, Context context) { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/scripts/waitFor.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # usage: 3 | # ./waitFor.sh joke operators Succeeded 4 | # ./waitFor.sh pod operators Running "name -o jsonpath='{.status.phase}'" 5 | 6 | RESOURCE="${1}" 7 | NAMESPACE="${2}" 8 | EXPECTED="${3}" 9 | EXTRA="${4-}" 10 | 11 | retries=30 12 | until [[ $retries == 0 ]]; do 13 | actual=$(kubectl get $RESOURCE -n $NAMESPACE $EXTRA 2>/dev/null || echo "Waiting for $RESOURCE -> $EXPECTED to appear") 14 | if [[ "$actual" =~ .*"$EXPECTED".* ]]; then 15 | echo "Resource \"$RESOURCE\" found with: $actual" 2>&1 16 | break 17 | else 18 | echo "Waiting for resource \"$RESOURCE\" actual: $actual" 2>&1 19 | fi 20 | sleep 10s 21 | retries=$((retries - 1)) 22 | done 23 | 24 | if [[ $retries == 0 ]]; then 25 | echo "Failed to get $RESOURCE" 2>&1 26 | exit 1 27 | else 28 | exit 0 29 | fi -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/V1Beta1CRD.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.client.CustomResource; 4 | import io.fabric8.kubernetes.model.annotation.Group; 5 | import io.fabric8.kubernetes.model.annotation.Kind; 6 | import io.fabric8.kubernetes.model.annotation.Version; 7 | 8 | @Group(V1Beta1CRD.GROUP) 9 | @Version(V1Beta1CRD.VERSION) 10 | @Kind(V1Beta1CRD.KIND) 11 | public class V1Beta1CRD extends CustomResource { 12 | 13 | public static final String GROUP = "test.com"; 14 | public static final String VERSION = "v2"; 15 | public static final String KIND = "V1Beta1"; 16 | public static final String CR_NAME = "v1beta1s." + GROUP; 17 | 18 | public record Spec(String value) { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /integration-tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.quarkiverse.operatorsdk 6 | quarkus-operator-sdk-build-parent 7 | 7.4.1-SNAPSHOT 8 | ../build-parent/pom.xml 9 | 10 | quarkus-operator-sdk-integration-tests-parent 11 | Quarkus - Operator SDK - Integration Tests 12 | pom 13 | 14 | basic 15 | cdi 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/exposedapp/src/main/java/io/halkyon/ExposedAppSpec.java: -------------------------------------------------------------------------------- 1 | package io.halkyon; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | public class ExposedAppSpec { 7 | 8 | // Add Spec information here 9 | private String imageRef; 10 | private Map env; 11 | 12 | private String endpoint; 13 | 14 | public String getEndpoint() { 15 | return endpoint; 16 | } 17 | 18 | public void setEndpoint(String endpoint) { 19 | this.endpoint = endpoint; 20 | } 21 | 22 | public String getImageRef() { 23 | return imageRef; 24 | } 25 | 26 | public void setImageRef(String imageRef) { 27 | this.imageRef = imageRef; 28 | } 29 | 30 | public Map getEnv() { 31 | return env == null ? Collections.emptyMap() : env; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/FirstReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 5 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 6 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 7 | 8 | @CSVMetadata(bundleName = "first-operator", version = FirstReconciler.VERSION) 9 | public class FirstReconciler implements Reconciler { 10 | 11 | public static final String VERSION = "first-version"; 12 | public static final String REPLACES = "first-replaces"; 13 | 14 | @Override 15 | public UpdateControl reconcile(First request, Context context) { 16 | return UpdateControl.noUpdate(); 17 | } 18 | } -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/OneNSReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 6 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 7 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 8 | 9 | @ControllerConfiguration(informer = @Informer(namespaces = OneNSReconciler.NS)) 10 | public class OneNSReconciler implements Reconciler { 11 | 12 | public static final String NS = "foo"; 13 | 14 | @Override 15 | public UpdateControl reconcile(TestCR testCR, Context context) throws Exception { 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/AnnotationConfigurablesBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import java.util.Map; 4 | 5 | import io.quarkiverse.operatorsdk.common.AnnotationConfigurableAugmentedClassInfo; 6 | import io.quarkus.builder.item.SimpleBuildItem; 7 | 8 | final class AnnotationConfigurablesBuildItem extends SimpleBuildItem { 9 | 10 | private final Map configurableInfos; 11 | 12 | public AnnotationConfigurablesBuildItem( 13 | Map configurableInfos) { 14 | this.configurableInfos = configurableInfos; 15 | } 16 | 17 | public Map getConfigurableInfos() { 18 | return configurableInfos; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.quarkiverse.operatorsdk 7 | quarkus-operator-sdk-build-parent 8 | 7.4.1-SNAPSHOT 9 | ../build-parent/pom.xml 10 | 11 | 12 | Quarkus - Operator SDK - Core - Parent 13 | quarkus-operator-sdk-core-parent 14 | pom 15 | 16 | 17 | deployment 18 | runtime 19 | 20 | 21 | -------------------------------------------------------------------------------- /helm/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.quarkiverse.operatorsdk 7 | quarkus-operator-sdk-build-parent 8 | 7.4.1-SNAPSHOT 9 | ../build-parent/pom.xml 10 | 11 | 12 | Quarkus - Operator SDK - Helm - Parent 13 | quarkus-operator-sdk-helm-parent 14 | pom 15 | 16 | 17 | deployment 18 | runtime 19 | 20 | 21 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/CSVMetadataBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.deployment; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import io.quarkiverse.operatorsdk.bundle.runtime.CSVMetadataHolder; 7 | import io.quarkiverse.operatorsdk.common.ReconcilerAugmentedClassInfo; 8 | import io.quarkus.builder.item.SimpleBuildItem; 9 | 10 | public final class CSVMetadataBuildItem extends SimpleBuildItem { 11 | private final Map> csvGroups; 12 | 13 | public CSVMetadataBuildItem(Map> csvGroups) { 14 | this.csvGroups = csvGroups; 15 | } 16 | 17 | public Map> getCsvGroups() { 18 | return csvGroups; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.quarkiverse.operatorsdk 6 | quarkus-operator-sdk-build-parent 7 | 7.4.1-SNAPSHOT 8 | ../build-parent/pom.xml 9 | 10 | 11 | Quarkus - Operator SDK - Common 12 | 13 | 14 | io.quarkus.qute 15 | qute-core 16 | 17 | 18 | quarkus-operator-sdk-common 19 | 20 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/ExternalGradualRetryConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import java.util.Optional; 4 | 5 | import io.quarkus.runtime.annotations.ConfigGroup; 6 | 7 | /** 8 | * Configuration for {@link io.javaoperatorsdk.operator.processing.retry.GradualRetry}. Will only apply if the 9 | * {@link io.javaoperatorsdk.operator.processing.retry.Retry} implementation class is 10 | * {@link io.javaoperatorsdk.operator.processing.retry.GenericRetry}. 11 | */ 12 | @ConfigGroup 13 | public interface ExternalGradualRetryConfiguration { 14 | 15 | /** 16 | * How many times an operation should be retried before giving up 17 | */ 18 | Optional maxAttempts(); 19 | 20 | /** 21 | * The configuration of the retry interval. 22 | */ 23 | ExternalGradualRetryIntervalConfiguration interval(); 24 | } 25 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/NameWithSpaceReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.ServiceAccount; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 6 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 7 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 8 | 9 | @ControllerConfiguration(name = NameWithSpaceReconciler.NAME) 10 | public class NameWithSpaceReconciler implements Reconciler { 11 | 12 | public static final String NAME = "name with space"; 13 | 14 | @Override 15 | public UpdateControl reconcile(ServiceAccount serviceAccount, 16 | Context context) throws Exception { 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bundle-generator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.quarkiverse.operatorsdk 7 | quarkus-operator-sdk-build-parent 8 | 7.4.1-SNAPSHOT 9 | ../build-parent/pom.xml 10 | 11 | 12 | Quarkus - Operator SDK - Bundle Generator - Parent 13 | quarkus-operator-sdk-bundle-generator-parent 14 | pom 15 | 16 | 17 | deployment 18 | runtime 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/dev-ui/logo_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/dev-ui/logo_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 10 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ApplicationScopedReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | 5 | import io.javaoperatorsdk.operator.api.reconciler.Context; 6 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 7 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 8 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 9 | 10 | @ApplicationScoped 11 | @ControllerConfiguration(name = ApplicationScopedReconciler.NAME) 12 | public class ApplicationScopedReconciler implements Reconciler { 13 | 14 | public static final String NAME = "ApplicationScoped"; 15 | 16 | @Override 17 | public UpdateControl reconcile(ChildTestResource childTestResource, 18 | Context context) { 19 | return UpdateControl.noUpdate(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Eclipse 26 | .project 27 | .classpath 28 | .settings/ 29 | bin/ 30 | 31 | # IntelliJ 32 | .idea 33 | *.ipr 34 | *.iml 35 | *.iws 36 | 37 | # NetBeans 38 | nb-configuration.xml 39 | 40 | # Visual Studio Code 41 | .vscode 42 | .factorypath 43 | 44 | # OSX 45 | .DS_Store 46 | 47 | # Vim 48 | *.swp 49 | *.swo 50 | 51 | # patch 52 | *.orig 53 | *.rej 54 | 55 | # Gradle 56 | .gradle/ 57 | build/ 58 | 59 | # Maven 60 | target/ 61 | pom.xml.tag 62 | pom.xml.releaseBackup 63 | pom.xml.versionsBackup 64 | release.properties 65 | 66 | # Quarkus 67 | .quarkus 68 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/ExternalGradualRetryIntervalConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import java.util.Optional; 4 | 5 | import io.quarkus.runtime.annotations.ConfigGroup; 6 | 7 | @ConfigGroup 8 | public interface ExternalGradualRetryIntervalConfiguration { 9 | long UNSET_INITIAL = -1; 10 | double UNSET_MULTIPLIER = -1; 11 | long UNSET_MAX = -1; 12 | 13 | /** 14 | * The initial interval that the controller waits for before attempting the first retry 15 | */ 16 | Optional initial(); 17 | 18 | /** 19 | * The value by which the initial interval is multiplied by for each retry 20 | */ 21 | Optional multiplier(); 22 | 23 | /** 24 | * The maximum interval that the controller will wait for before attempting a retry, regardless of 25 | * all other configuration 26 | */ 27 | Optional max(); 28 | } 29 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/ConfiguredReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 6 | import io.quarkiverse.operatorsdk.runtime.api.ConfigurableReconciler; 7 | 8 | public class ConfiguredReconciler implements ConfigurableReconciler { 9 | 10 | public static final String LABEL_SELECTOR = "foo=bar"; 11 | 12 | @Override 13 | public UpdateControl reconcile(TestCR testCR, Context context) throws Exception { 14 | return null; 15 | } 16 | 17 | @Override 18 | public void updateConfigurationFrom(ControllerConfigurationOverrider configOverrider) { 19 | configOverrider.withLabelSelector(LABEL_SELECTOR); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/exposedapp/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # set to true to automatically apply CRDs to the cluster when they get regenerated 2 | quarkus.operator-sdk.crd.apply=true 3 | %test.quarkus.operator-sdk.close-client-on-stop=false 4 | %test.quarkus.operator-sdk.start-operator=true 5 | 6 | quarkus.container-image.builder=jib 7 | # Test configuration, feel free to change it to your preferences 8 | quarkus.container-image.image=quay.io/xstefank/quarkus-operator-sdk-samples-exposedapp:2025-08-13 9 | 10 | # Uncomment the following line to enable building the container image during the build phase 11 | #quarkus.container-image.build=true 12 | 13 | quarkus.kubernetes.namespace=default 14 | quarkus.kubernetes.image-pull-policy=IfNotPresent 15 | 16 | quarkus.kubernetes-client.devservices.override-kubeconfig=true 17 | quarkus.kubernetes-client.devservices.flavor=k3s 18 | quarkus.kubernetes-client.devservices.enabled=true 19 | 20 | quarkus.http.test-port=0 21 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/QOSDKReflectiveClassBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import java.util.List; 4 | import java.util.stream.Stream; 5 | 6 | import io.quarkus.builder.item.MultiBuildItem; 7 | 8 | final class QOSDKReflectiveClassBuildItem extends MultiBuildItem { 9 | 10 | private final List classNamesToRegisterForReflection; 11 | 12 | public QOSDKReflectiveClassBuildItem(List classNamesToRegisterForReflection) { 13 | this(classNamesToRegisterForReflection.toArray(new String[0])); 14 | } 15 | 16 | public QOSDKReflectiveClassBuildItem(String... classNameToRegisterForReflection) { 17 | this.classNamesToRegisterForReflection = List.of(classNameToRegisterForReflection); 18 | } 19 | 20 | public Stream classNamesToRegisterForReflectionStream() { 21 | return classNamesToRegisterForReflection.stream(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/EmptyReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import static io.quarkiverse.operatorsdk.it.EmptyReconciler.NAME; 4 | 5 | import io.javaoperatorsdk.operator.api.reconciler.Context; 6 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 7 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 8 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 9 | 10 | @ControllerConfiguration(name = NAME) 11 | public class EmptyReconciler implements Reconciler { 12 | 13 | public static final String NAME = "empty"; 14 | static final String FROM_ENV_NS1 = "fromEnv1"; 15 | static final String FROM_ENV_NS2 = "fromEnv2"; 16 | 17 | @Override 18 | public UpdateControl reconcile(EmptyCR emptyCR, Context context) 19 | throws Exception { 20 | return UpdateControl.noUpdate(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/OptionalImplicitNativeDependent.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.Secret; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 6 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; 7 | import io.javaoperatorsdk.operator.processing.dependent.workflow.CRDPresentActivationCondition; 8 | 9 | public class OptionalImplicitNativeDependent extends KubernetesDependentResource { 10 | public static class ActivationCondition extends CRDPresentActivationCondition { 11 | @Override 12 | public boolean isMet(DependentResource dependentResource, Third primary, Context context) { 13 | return false; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/MicrometerMetricsProvider.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import jakarta.enterprise.inject.Produces; 4 | import jakarta.inject.Singleton; 5 | 6 | import io.javaoperatorsdk.operator.api.monitoring.Metrics; 7 | import io.javaoperatorsdk.operator.monitoring.micrometer.MicrometerMetrics; 8 | import io.micrometer.core.instrument.MeterRegistry; 9 | import io.micrometer.core.instrument.binder.MeterBinder; 10 | import io.quarkus.arc.DefaultBean; 11 | 12 | @Singleton 13 | public class MicrometerMetricsProvider implements MeterBinder { 14 | private Metrics metrics = Metrics.NOOP; 15 | 16 | @Override 17 | public void bindTo(MeterRegistry registry) { 18 | metrics = MicrometerMetrics.newPerResourceCollectingMicrometerMetricsBuilder(registry).build(); 19 | } 20 | 21 | @Produces 22 | @DefaultBean 23 | public Metrics getMetrics() { 24 | return metrics; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/devmode/OperatorSDKHotReplacementSetup.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime.devmode; 2 | 3 | import java.util.concurrent.Executors; 4 | import java.util.concurrent.ScheduledExecutorService; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import io.quarkus.dev.spi.HotReplacementContext; 8 | import io.quarkus.dev.spi.HotReplacementSetup; 9 | 10 | public class OperatorSDKHotReplacementSetup implements HotReplacementSetup { 11 | private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 12 | 13 | @Override 14 | public void setupHotDeployment(HotReplacementContext context) { 15 | executor.scheduleAtFixedRate(() -> { 16 | try { 17 | context.doScan(false); 18 | } catch (Exception e) { 19 | throw new IllegalStateException(e); 20 | } 21 | }, 10, 10, TimeUnit.SECONDS); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/KeycloakController.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 4 | import io.javaoperatorsdk.operator.api.reconciler.Constants; 5 | import io.javaoperatorsdk.operator.api.reconciler.Context; 6 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 7 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 8 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 9 | 10 | @ControllerConfiguration(informer = @Informer(namespaces = Constants.WATCH_CURRENT_NAMESPACE)) 11 | public class KeycloakController implements Reconciler { 12 | 13 | public static final String FROM_ENV = Constants.WATCH_ALL_NAMESPACES; 14 | 15 | @Override 16 | public UpdateControl reconcile(Keycloak keycloak, Context context) 17 | throws Exception { 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/AReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.ConfigMap; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 8 | import io.quarkiverse.operatorsdk.annotations.SharedCSVMetadata; 9 | 10 | @CSVMetadata(bundleName = AReconciler.SHARED, version = AReconciler.SHARED_VERSION) 11 | public class AReconciler implements Reconciler, SharedCSVMetadata { 12 | 13 | public static final String SHARED_VERSION = "0.0.1"; 14 | public static final String SHARED = "shared"; 15 | 16 | @Override 17 | public UpdateControl reconcile(ConfigMap configMap, Context context) { 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /common-deployment/src/main/java/io/quarkiverse/operatorsdk/common/DeserializedKubernetesResourcesBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.common; 2 | 3 | import java.util.List; 4 | 5 | import io.fabric8.kubernetes.api.model.HasMetadata; 6 | import io.quarkus.builder.item.SimpleBuildItem; 7 | 8 | public final class DeserializedKubernetesResourcesBuildItem extends SimpleBuildItem { 9 | private final List resources; 10 | 11 | public DeserializedKubernetesResourcesBuildItem(List resources) { 12 | this.resources = resources; 13 | } 14 | 15 | /** 16 | * Note that these resources are "live" so any modification made to them will be propagated anywhere this method is called 17 | * and a reference on the result is kept so if you don't want local changes to be propagated, make a copy of the resources 18 | * you interact with, first. 19 | */ 20 | public List getResources() { 21 | return resources; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/AddNamespaceEnvVarBuildStep.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import io.quarkiverse.operatorsdk.common.ConfigurationUtils; 4 | import io.quarkus.deployment.annotations.BuildProducer; 5 | import io.quarkus.deployment.annotations.BuildStep; 6 | import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem; 7 | 8 | public class AddNamespaceEnvVarBuildStep { 9 | @BuildStep 10 | void addNamespaceEnvVar(ControllerConfigurationsBuildItem controllers, 11 | BuildProducer envVarProducer) { 12 | controllers.getControllerConfigs().values().forEach(config -> { 13 | final var key = ConfigurationUtils.getNamespacesPropertyName(config.getName(), true); 14 | final var value = String.join(",", config.getInformerConfig().getNamespaces()); 15 | envVarProducer.produce(KubernetesEnvBuildItem.createSimpleVar(key, value, null)); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ReconcilerWithExternalCR.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 5 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 6 | import io.javaoperatorsdk.operator.api.reconciler.Workflow; 7 | import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; 8 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 9 | 10 | @Workflow(dependents = @Dependent(type = ExternalDependentResource.class)) 11 | @CSVMetadata(requiredCRDs = @CSVMetadata.RequiredCRD(kind = V1Beta1CRD.KIND, name = V1Beta1CRD.CR_NAME, version = V1Beta1CRD.VERSION)) 12 | public class ReconcilerWithExternalCR implements Reconciler { 13 | @Override 14 | public UpdateControl reconcile(First first, Context context) throws Exception { 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/VariableNSReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.apps.Deployment; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 6 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 7 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 8 | 9 | @ControllerConfiguration(name = VariableNSReconciler.NAME) 10 | public class VariableNSReconciler implements Reconciler { 11 | 12 | public static final String NAME = "variablens"; 13 | public static final String ENV_VAR_NAME = "VARIABLE_NS_ENV"; 14 | public static final String EXPECTED_NS_VALUE = "variableNSFromEnv"; 15 | 16 | @Override 17 | public UpdateControl reconcile(Deployment deployment, Context context) 18 | throws Exception { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/CreateOnlyService.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.Service; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 6 | import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; 7 | import io.javaoperatorsdk.operator.processing.dependent.Creator; 8 | 9 | public class CreateOnlyService implements DependentResource, Creator { 10 | 11 | @Override 12 | public ReconcileResult reconcile(TestCR testCR, Context context) { 13 | return null; 14 | } 15 | 16 | @Override 17 | public Class resourceType() { 18 | return Service.class; 19 | } 20 | 21 | @Override 22 | public Service create(Service service, TestCR testCR, Context context) { 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/TestReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import io.javaoperatorsdk.operator.api.reconciler.Context; 6 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 7 | import io.javaoperatorsdk.operator.api.reconciler.MaxReconciliationInterval; 8 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 9 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 10 | 11 | @ControllerConfiguration(name = TestReconciler.NAME, maxReconciliationInterval = @MaxReconciliationInterval(interval = TestReconciler.INTERVAL, timeUnit = TimeUnit.SECONDS)) 12 | public class TestReconciler implements Reconciler { 13 | public static final String NAME = "test"; 14 | public static final int INTERVAL = 50; 15 | 16 | @Override 17 | public UpdateControl reconcile(Test test, Context context) throws Exception { 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/WatchAllNamespacesReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_ALL_NAMESPACES; 4 | 5 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 6 | import io.javaoperatorsdk.operator.api.reconciler.Context; 7 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 8 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 9 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 10 | 11 | @ControllerConfiguration(name = WatchAllNamespacesReconciler.NAME, informer = @Informer(namespaces = WATCH_ALL_NAMESPACES)) 12 | public class WatchAllNamespacesReconciler implements Reconciler { 13 | 14 | public static final String NAME = "all-namespaces-reconciler"; 15 | 16 | @Override 17 | public UpdateControl reconcile(SimpleCR simpleCR, Context context) { 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/DeletePostCondition.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | import jakarta.inject.Inject; 5 | 6 | import io.fabric8.kubernetes.api.model.HasMetadata; 7 | import io.javaoperatorsdk.operator.api.reconciler.Context; 8 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 9 | import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; 10 | import io.quarkus.arc.Unremovable; 11 | 12 | @ApplicationScoped 13 | @Unremovable 14 | public class DeletePostCondition implements Condition { 15 | 16 | @Inject 17 | TestUUIDBean testUUIDBean; 18 | 19 | private String uuid; 20 | 21 | @Override 22 | public boolean isMet(DependentResource dependentResource, HasMetadata primary, Context context) { 23 | this.uuid = testUUIDBean.uuid(); 24 | return true; 25 | } 26 | 27 | public String getUuid() { 28 | return uuid; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/ReconcilePrecondition.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | import jakarta.inject.Inject; 5 | 6 | import io.fabric8.kubernetes.api.model.HasMetadata; 7 | import io.javaoperatorsdk.operator.api.reconciler.Context; 8 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 9 | import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; 10 | import io.quarkus.arc.Unremovable; 11 | 12 | @ApplicationScoped 13 | @Unremovable 14 | public class ReconcilePrecondition implements Condition { 15 | 16 | @Inject 17 | TestUUIDBean testUUIDBean; 18 | 19 | private String uuid; 20 | 21 | @Override 22 | 23 | public boolean isMet(DependentResource dependentResource, HasMetadata primary, Context context) { 24 | uuid = testUUIDBean.uuid(); 25 | return true; 26 | } 27 | 28 | public String getUuid() { 29 | return uuid; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/exposedapp/src/main/java/io/halkyon/ExposedAppStatus.java: -------------------------------------------------------------------------------- 1 | package io.halkyon; 2 | 3 | @SuppressWarnings("unused") 4 | public class ExposedAppStatus { 5 | 6 | private String host; 7 | private String message; 8 | 9 | public ExposedAppStatus() { 10 | message = "processing"; 11 | } 12 | 13 | public ExposedAppStatus(String hostname, String endpoint) { 14 | if (hostname == null || endpoint == null) { 15 | this.message = "reconciled-not-exposed"; 16 | } else { 17 | this.message = "exposed"; 18 | this.host = endpoint != null && !endpoint.isBlank() ? hostname + '/' + endpoint : hostname; 19 | } 20 | } 21 | 22 | public String getHost() { 23 | return host; 24 | } 25 | 26 | public void setHost(String host) { 27 | this.host = host; 28 | } 29 | 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | public void setMessage(String message) { 35 | this.message = message; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip 20 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/CustomActionvationCondition.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | import jakarta.inject.Inject; 5 | 6 | import io.fabric8.kubernetes.api.model.HasMetadata; 7 | import io.javaoperatorsdk.operator.api.reconciler.Context; 8 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 9 | import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; 10 | import io.quarkus.arc.Unremovable; 11 | 12 | @ApplicationScoped 13 | @Unremovable 14 | public class CustomActionvationCondition implements Condition { 15 | 16 | @Inject 17 | TestUUIDBean testUUIDBean; 18 | 19 | private String uuid; 20 | 21 | @Override 22 | public boolean isMet(DependentResource dependentResource, HasMetadata primary, Context context) { 23 | uuid = testUUIDBean.uuid(); 24 | return true; 25 | } 26 | 27 | public String getUuid() { 28 | return uuid; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/ControllerConfigurationsBuildItem.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import io.quarkiverse.operatorsdk.runtime.QuarkusBuildTimeControllerConfiguration; 8 | import io.quarkus.builder.item.SimpleBuildItem; 9 | 10 | @SuppressWarnings("rawtypes") 11 | public final class ControllerConfigurationsBuildItem extends SimpleBuildItem { 12 | 13 | private final Map> controllerConfigs; 14 | 15 | public ControllerConfigurationsBuildItem(List controllerConfigs) { 16 | this.controllerConfigs = new HashMap<>(controllerConfigs.size()); 17 | 18 | controllerConfigs.forEach(c -> this.controllerConfigs.put(c.getName(), c)); 19 | } 20 | 21 | public Map> getControllerConfigs() { 22 | return controllerConfigs; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/WatchAllReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.fabric8.kubernetes.api.model.ConfigMap; 4 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 5 | import io.javaoperatorsdk.operator.api.reconciler.Constants; 6 | import io.javaoperatorsdk.operator.api.reconciler.Context; 7 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 8 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 9 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 10 | 11 | @ControllerConfiguration(name = WatchAllReconciler.NAME, informer = @Informer(namespaces = Constants.WATCH_ALL_NAMESPACES)) 12 | public class WatchAllReconciler implements Reconciler { 13 | 14 | public static final String NAME = "watchall"; 15 | 16 | @Override 17 | public UpdateControl reconcile(ConfigMap configMap, Context context) throws Exception { 18 | return UpdateControl.noUpdate(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /helm/deployment/src/test/java/io/quarkiverse/operatorsdk/helm/deployment/test/sources/WatchAllNamespacesReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.helm.deployment.test.sources; 2 | 3 | import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_ALL_NAMESPACES; 4 | 5 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 6 | import io.javaoperatorsdk.operator.api.reconciler.Context; 7 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 8 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 9 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 10 | 11 | @ControllerConfiguration(name = WatchAllNamespacesReconciler.NAME, informer = @Informer(namespaces = WATCH_ALL_NAMESPACES)) 12 | public class WatchAllNamespacesReconciler implements Reconciler { 13 | 14 | public static final String NAME = "all-namespaces-reconciler"; 15 | 16 | @Override 17 | public UpdateControl reconcile(SimpleCR simpleCR, Context context) { 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/mysql-schema/src/main/java/io/quarkiverse/operatorsdk/samples/mysqlschema/SchemaStatus.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.mysqlschema; 2 | 3 | public class SchemaStatus { 4 | 5 | private String url; 6 | 7 | private String status; 8 | 9 | private String userName; 10 | 11 | private String secretName; 12 | 13 | public String getUrl() { 14 | return url; 15 | } 16 | 17 | public void setUrl(String url) { 18 | this.url = url; 19 | } 20 | 21 | public String getStatus() { 22 | return status; 23 | } 24 | 25 | public void setStatus(String status) { 26 | this.status = status; 27 | } 28 | 29 | public String getUserName() { 30 | return userName; 31 | } 32 | 33 | public void setUserName(String userName) { 34 | this.userName = userName; 35 | } 36 | 37 | public String getSecretName() { 38 | return secretName; 39 | } 40 | 41 | public void setSecretName(String secretName) { 42 | this.secretName = secretName; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/devui/commands/ConsoleCommands.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment.devui.commands; 2 | 3 | import java.util.List; 4 | 5 | import org.aesh.command.*; 6 | import org.aesh.command.invocation.CommandInvocation; 7 | 8 | import io.quarkiverse.operatorsdk.runtime.Version; 9 | 10 | @GroupCommandDefinition(name = "qosdk", description = "Quarkus Operator SDK Commands") 11 | @SuppressWarnings("rawtypes") 12 | public class ConsoleCommands implements GroupCommand { 13 | private final Version version; 14 | 15 | public ConsoleCommands(Version version) { 16 | this.version = version; 17 | } 18 | 19 | @Override 20 | public List getCommands() { 21 | return List.of(new VersionsCommand(version), new APICommand()); 22 | } 23 | 24 | @Override 25 | public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException { 26 | commandInvocation.println(commandInvocation.getHelpInfo()); 27 | return CommandResult.SUCCESS; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/CustomRateLimiterReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.ResourceQuota; 4 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 5 | import io.javaoperatorsdk.operator.api.reconciler.Context; 6 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 7 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 8 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 9 | 10 | @ControllerConfiguration(name = CustomRateLimiterReconciler.NAME, rateLimiter = CustomRateLimiter.class, informer = @Informer(itemStore = NullItemStore.class)) 11 | @CustomRateConfiguration(42) 12 | public class CustomRateLimiterReconciler implements Reconciler { 13 | 14 | public static final String NAME = "CustomRateLimiter"; 15 | 16 | @Override 17 | public UpdateControl reconcile(ResourceQuota resourceQuota, Context context) 18 | throws Exception { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/OperatorSDKDevModeTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test; 2 | 3 | import org.jboss.shrinkwrap.api.ShrinkWrap; 4 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Disabled; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.RegisterExtension; 9 | 10 | import io.quarkus.test.QuarkusDevModeTest; 11 | 12 | @Disabled 13 | public class OperatorSDKDevModeTest { 14 | 15 | // Start hot reload (DevMode) test with your extension loaded 16 | @RegisterExtension 17 | static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() 18 | .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); 19 | 20 | @Test 21 | public void writeYourOwnDevModeTest() { 22 | // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information 23 | Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/KubernetesLabelConfigOverrider.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.deployment; 2 | 3 | import java.util.Map; 4 | 5 | import io.smallrye.config.PropertiesConfigSource; 6 | import io.smallrye.config.SmallRyeConfigBuilder; 7 | import io.smallrye.config.SmallRyeConfigBuilderCustomizer; 8 | 9 | /** 10 | * Overrides the default value for the Kubernetes extension-provided {@code add-version-to-label-selectors} when the bundle 11 | * generator is used. See this issue for more 12 | * details. 13 | */ 14 | public class KubernetesLabelConfigOverrider implements SmallRyeConfigBuilderCustomizer { 15 | @Override 16 | public void configBuilder(SmallRyeConfigBuilder builder) { 17 | // not sure what value for the priority should be used since they seem pretty random 18 | builder.withSources(new PropertiesConfigSource(Map.of("quarkus.kubernetes.add-version-to-label-selectors", "false"), 19 | "KubernetesLabelConfigOverrider", 50)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/io/quarkiverse/operatorsdk/common/CLIConstants.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.common; 2 | 3 | public class CLIConstants { 4 | public static final String API_DESCRIPTION = "Creates a Kubernetes API based on a Fabric8 CustomResource along with the associated JOSDK Reconciler"; 5 | 6 | public static final char API_KIND_SHORT = 'k'; 7 | public static final String API_KIND = "kind"; 8 | public static final String API_KIND_DESCRIPTION = "Your API's kind, used to generate a CustomResource class with associated spec, status and Reconciler classes"; 9 | 10 | public static final char API_VERSION_SHORT = 'v'; 11 | public static final String API_VERSION = "version"; 12 | public static final String API_VERSION_DESCRIPTION = "Your API's version, e.g. v1beta1"; 13 | 14 | public static final char API_GROUP_SHORT = 'g'; 15 | public static final String API_GROUP = "group"; 16 | public static final String API_GROUP_DESCRIPTION = "Your API's group, e.g. halkyon.io, this will also be used, reversed, as package name for the generated classes"; 17 | 18 | private CLIConstants() { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/devui/JSONRPCService.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime.devui; 2 | 3 | import java.util.Collection; 4 | import java.util.stream.Collectors; 5 | 6 | import jakarta.enterprise.context.ApplicationScoped; 7 | import jakarta.inject.Inject; 8 | 9 | import io.javaoperatorsdk.operator.Operator; 10 | import io.javaoperatorsdk.operator.processing.Controller; 11 | import io.quarkiverse.operatorsdk.runtime.devconsole.ControllerInfo; 12 | 13 | @ApplicationScoped 14 | public class JSONRPCService { 15 | @Inject 16 | Operator operator; 17 | 18 | @SuppressWarnings({ "rawtypes", "unchecked" }) 19 | public Collection getControllers() { 20 | return operator.getRegisteredControllers().stream() 21 | .map(Controller.class::cast) 22 | .map(registeredController -> new ControllerInfo(registeredController)) 23 | .collect(Collectors.toList()); 24 | } 25 | 26 | @SuppressWarnings("unused") 27 | public int controllersCount() { 28 | return operator.getRegisteredControllersNumber(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/NamespaceFromEnvReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.Pod; 4 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 5 | import io.javaoperatorsdk.operator.api.reconciler.Context; 6 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 7 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 8 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 9 | 10 | @ControllerConfiguration(name = NamespaceFromEnvReconciler.NAME, informer = @Informer(namespaces = { "static", 11 | "${" + NamespaceFromEnvReconciler.ENV_VAR_NAME + "}" })) 12 | public class NamespaceFromEnvReconciler implements Reconciler { 13 | public static final String NAME = "fromenv"; 14 | public static final String ENV_VAR_NAME = "NAMESPACE_FROM_ENV"; 15 | static final String FROM_ENV_VAR_NS = "fromEnvVarNS"; 16 | 17 | @Override 18 | public UpdateControl reconcile(Pod pod, Context context) throws Exception { 19 | return UpdateControl.noUpdate(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /bundle-generator/runtime/src/main/java/io/quarkiverse/operatorsdk/bundle/runtime/BundleConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.runtime; 2 | 3 | import java.util.Map; 4 | 5 | import io.quarkus.runtime.annotations.ConfigGroup; 6 | 7 | @ConfigGroup 8 | public interface BundleConfiguration { 9 | String OLM_SKIP_RANGE_ANNOTATION = "olm.skipRange"; 10 | String CONTAINER_IMAGE_ANNOTATION = "containerImage"; 11 | String REPOSITORY_ANNOTATION = "repository"; 12 | String CAPABILITIES_ANNOTATION = "capabilities"; 13 | String CATEGORIES_ANNOTATION = "categories"; 14 | String CERTIFIED_ANNOTATION = "certified"; 15 | String ALM_EXAMPLES_ANNOTATION = "alm-examples"; 16 | 17 | /** 18 | * The bundle's annotations (as found in the CSV metadata) 19 | */ 20 | Map annotations(); 21 | 22 | default void mergeWithDefaults(BundleConfiguration defaults) { 23 | final var annotations = annotations(); 24 | if (annotations != null) { 25 | annotations.keySet().forEach(key -> annotations.computeIfAbsent(key, k -> defaults.annotations().get(k))); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/AnnotatedDependentReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import static io.quarkiverse.operatorsdk.it.AnnotatedDependentReconciler.NAME; 4 | 5 | import io.fabric8.kubernetes.api.model.Service; 6 | import io.javaoperatorsdk.operator.api.reconciler.Context; 7 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 8 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 9 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 10 | import io.javaoperatorsdk.operator.api.reconciler.Workflow; 11 | import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; 12 | 13 | @Workflow(dependents = @Dependent(type = AnnotatedDependentResource.class)) 14 | @ControllerConfiguration(name = NAME) 15 | public class AnnotatedDependentReconciler implements Reconciler { 16 | 17 | public static final String NAME = "annotated-dependent"; 18 | 19 | @Override 20 | public UpdateControl reconcile(Service service, Context context) 21 | throws Exception { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/external-crd/src/test/java/io/quarkiverse/operatorsdk/it/ExternalCRDTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import jakarta.inject.Inject; 4 | 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; 9 | import io.fabric8.kubernetes.client.KubernetesClient; 10 | import io.javaoperatorsdk.operator.Operator; 11 | import io.quarkus.test.junit.QuarkusTest; 12 | 13 | @QuarkusTest 14 | public class ExternalCRDTest { 15 | @Inject 16 | KubernetesClient unused; // needed to start kube dev service 17 | 18 | @Inject 19 | Operator operator; 20 | 21 | @Test 22 | void externalCRDIsDeployed() { 23 | // start the operator 24 | operator.start(); 25 | 26 | // external CRD should be deployed 27 | var externalCRD = operator.getKubernetesClient() 28 | .resources(CustomResourceDefinition.class) 29 | .withName("externals.halkyon.io") 30 | .get(); 31 | Assertions.assertNotNull(externalCRD); 32 | 33 | operator.stop(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /annotations/src/main/java/io/quarkiverse/operatorsdk/annotations/RBACVerbs.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.annotations; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | 6 | public class RBACVerbs { 7 | public static final String CREATE = "create"; 8 | public static final String PATCH = "patch"; 9 | public static final String UPDATE = "update"; 10 | public static final String GET = "get"; 11 | public static final String LIST = "list"; 12 | public static final String WATCH = "watch"; 13 | public static final String DELETE = "delete"; 14 | public static final String[] UPDATE_VERBS = new String[] { PATCH, UPDATE }; 15 | public static final String[] READ_VERBS = new String[] { GET, LIST, WATCH }; 16 | public static final String[] ALL_COMMON_VERBS; 17 | 18 | static { 19 | final var verbs = new ArrayList(READ_VERBS.length + UPDATE_VERBS.length + 2); 20 | verbs.addAll(Arrays.asList(READ_VERBS)); 21 | verbs.addAll(Arrays.asList(UPDATE_VERBS)); 22 | verbs.add(CREATE); 23 | verbs.add(DELETE); 24 | ALL_COMMON_VERBS = verbs.toArray(new String[0]); 25 | } 26 | 27 | private RBACVerbs() { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/io/quarkiverse/operatorsdk/common/ClassLoadingUtils.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.common; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | 5 | public class ClassLoadingUtils { 6 | 7 | private ClassLoadingUtils() { 8 | } 9 | 10 | @SuppressWarnings({ "unchecked", "unused" }) 11 | public static Class loadClass(String className, Class expected) { 12 | try { 13 | return (Class) Thread.currentThread().getContextClassLoader().loadClass(className); 14 | } catch (Exception e) { 15 | throw new IllegalArgumentException("Couldn't find class " + className, e); 16 | } 17 | } 18 | 19 | public static T instantiate(Class toInstantiate) { 20 | try { 21 | final var constructor = toInstantiate.getConstructor(); 22 | constructor.setAccessible(true); 23 | return constructor.newInstance(); 24 | } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 25 | throw new IllegalArgumentException("Couldn't instantiate " + toInstantiate.getName(), 26 | e); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "metacosm", 10 | "name": "Chris Laprun", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/120057?v=4", 12 | "profile": "http://about.me/metacosm", 13 | "contributions": [ 14 | "code", 15 | "maintenance" 16 | ] 17 | }, 18 | { 19 | "login": "scrocquesel", 20 | "name": "Sébastien CROCQUESEL", 21 | "avatar_url": "https://avatars.githubusercontent.com/u/88554524?v=4", 22 | "profile": "https://www.inulogic.fr", 23 | "contributions": [ 24 | "code" 25 | ] 26 | }, 27 | { 28 | "login": "Sgitario", 29 | "name": "Jose Carvajal", 30 | "avatar_url": "https://avatars.githubusercontent.com/u/6310047?v=4", 31 | "profile": "https://sgitario.github.io/about/", 32 | "contributions": [ 33 | "code", 34 | "ideas" 35 | ] 36 | } 37 | ], 38 | "contributorsPerLine": 7, 39 | "projectName": "quarkus-operator-sdk", 40 | "projectOwner": "quarkiverse", 41 | "repoType": "github", 42 | "repoHost": "https://github.com", 43 | "skipCi": true 44 | } 45 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/CRUDDependentResource.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.ConfigMap; 4 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 5 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; 6 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; 7 | import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; 8 | import io.quarkiverse.operatorsdk.it.CRUDDependentResource.TestOnAddFilter; 9 | 10 | @KubernetesDependent(informer = @Informer(labelSelector = CRUDDependentResource.LABEL_SELECTOR, onAddFilter = TestOnAddFilter.class)) 11 | public class CRUDDependentResource extends CRUDKubernetesDependentResource { 12 | 13 | public static class TestOnAddFilter implements OnAddFilter { 14 | 15 | @Override 16 | public boolean accept(ConfigMap configMap) { 17 | return true; 18 | } 19 | } 20 | 21 | public static final String LABEL_SELECTOR = "environment=production,foo=bar"; 22 | 23 | public CRUDDependentResource() { 24 | super(ConfigMap.class); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common-deployment/src/main/java/io/quarkiverse/operatorsdk/common/FileUtils.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.common; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.File; 5 | 6 | import io.fabric8.kubernetes.client.utils.KubernetesSerialization; 7 | 8 | public class FileUtils { 9 | private final static KubernetesSerialization serializer = new KubernetesSerialization(); 10 | 11 | public static void ensureDirectoryExists(File dir) { 12 | if (!dir.exists()) { 13 | if (!dir.mkdirs()) { 14 | throw new IllegalArgumentException("Couldn't create " + dir.getAbsolutePath()); 15 | } 16 | } 17 | } 18 | 19 | /** 20 | * Unmarshal the given YAML or JSON to an object. The returned object will be a list if the input 21 | * is a YAML/JSON array. 22 | */ 23 | public static Object unmarshalFrom(byte[] yamlOrJson) { 24 | return serializer.unmarshal(new ByteArrayInputStream(yamlOrJson)); 25 | } 26 | 27 | /** 28 | * Serialize the given object to YAML. If the given object is a list, the returned YAML will be a 29 | * YAML array. 30 | */ 31 | public static String asYaml(Object toSerialize) { 32 | return serializer.asYaml(toSerialize); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/BuildTimeConfigurationServiceBuildStep.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import io.quarkiverse.operatorsdk.runtime.BuildTimeConfigurationService; 4 | import io.quarkiverse.operatorsdk.runtime.BuildTimeOperatorConfiguration; 5 | import io.quarkus.deployment.annotations.BuildStep; 6 | 7 | public class BuildTimeConfigurationServiceBuildStep { 8 | 9 | @BuildStep 10 | BuildTimeConfigurationServiceBuildItem createBuildTimeConfigurationService(VersionBuildItem versionBuildItem, 11 | GeneratedCRDInfoBuildItem generatedCRDs, BuildTimeOperatorConfiguration buildTimeConfig) { 12 | final var service = new BuildTimeConfigurationService( 13 | versionBuildItem.getVersion(), 14 | generatedCRDs.getCRDGenerationInfo(), 15 | buildTimeConfig.startOperator(), 16 | buildTimeConfig.closeClientOnStop(), 17 | buildTimeConfig.stopOnInformerErrorDuringStartup(), 18 | buildTimeConfig.enableSSA(), 19 | buildTimeConfig.activateLeaderElectionForProfiles(), 20 | buildTimeConfig.defensiveCloning()); 21 | return new BuildTimeConfigurationServiceBuildItem(service); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/ReadyPostCondition.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | import jakarta.inject.Inject; 5 | 6 | import io.fabric8.kubernetes.api.model.apps.Deployment; 7 | import io.javaoperatorsdk.operator.api.reconciler.Context; 8 | import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; 9 | import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; 10 | import io.quarkus.arc.Unremovable; 11 | 12 | @ApplicationScoped 13 | @Unremovable 14 | public class ReadyPostCondition implements Condition { 15 | 16 | @Inject 17 | TestUUIDBean testUUIDBean; 18 | 19 | private String uuid; 20 | 21 | @Override 22 | public boolean isMet(DependentResource dependentResource, TestResource primary, 23 | Context context) { 24 | uuid = testUUIDBean.uuid(); 25 | return dependentResource 26 | .getSecondaryResource(primary, context) 27 | .map(deployment -> deployment.getSpec().getReplicas().equals(1)) 28 | .orElse(false); 29 | } 30 | 31 | public String getUuid() { 32 | return uuid; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/joke/src/main/java/io/quarkiverse/operatorsdk/samples/joke/JokeRequest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Red Hat, Inc. and/or its affiliates. 3 | * 4 | *

5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | */ 15 | package io.quarkiverse.operatorsdk.samples.joke; 16 | 17 | import io.fabric8.kubernetes.api.model.Namespaced; 18 | import io.fabric8.kubernetes.client.CustomResource; 19 | import io.fabric8.kubernetes.model.annotation.Group; 20 | import io.fabric8.kubernetes.model.annotation.ShortNames; 21 | import io.fabric8.kubernetes.model.annotation.Version; 22 | 23 | @Group("samples.javaoperatorsdk.io") 24 | @Version("v1alpha1") 25 | @ShortNames("jr") 26 | public class JokeRequest extends CustomResource implements Namespaced { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/SecondReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 6 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 7 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 8 | import io.quarkiverse.operatorsdk.annotations.CSVMetadata; 9 | import io.quarkiverse.operatorsdk.annotations.RBACRule; 10 | 11 | @CSVMetadata(bundleName = "second-operator") 12 | @RBACRule(apiGroups = SecondReconciler.RBAC_RULE_GROUP, resources = SecondReconciler.RBAC_RULE_RES, verbs = SecondReconciler.RBAC_RULE_VERBS) 13 | @ControllerConfiguration(informer = @Informer(namespaces = "foo")) 14 | public class SecondReconciler implements Reconciler { 15 | public static final String RBAC_RULE_GROUP = "halkyon.io"; 16 | public static final String RBAC_RULE_RES = "SomeResource"; 17 | public static final String RBAC_RULE_VERBS = "write"; 18 | 19 | @Override 20 | public UpdateControl reconcile(Second request, Context context) { 21 | return UpdateControl.noUpdate(); 22 | } 23 | } -------------------------------------------------------------------------------- /.github/workflows/release-perform.yml: -------------------------------------------------------------------------------- 1 | name: Quarkiverse Perform Release 2 | run-name: Perform ${{github.event.inputs.tag || github.ref_name}} Release 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | workflow_dispatch: 8 | inputs: 9 | tag: 10 | type: string 11 | description: 'Tag to release' 12 | required: true 13 | ref: 14 | description: 'Branch or tag to deploy' 15 | type: string 16 | required: false 17 | dry_run: 18 | description: 'Dry run the release' 19 | required: false 20 | type: boolean 21 | default: false 22 | 23 | permissions: 24 | attestations: write 25 | id-token: write 26 | contents: read 27 | 28 | concurrency: 29 | group: ${{ github.workflow }}-${{ github.ref }} 30 | cancel-in-progress: true 31 | 32 | jobs: 33 | perform-release: 34 | name: Perform Release 35 | uses: quarkiverse/.github/.github/workflows/perform-release.yml@main 36 | secrets: inherit 37 | with: 38 | version: ${{github.event.inputs.tag || github.ref_name}} 39 | 40 | update-quarkus-platform: 41 | needs: 42 | - perform-release 43 | uses: ./.github/workflows/release-update-quarkus-platform.yml 44 | with: 45 | tag: ${{github.event.inputs.tag || github.ref_name}} 46 | secrets: inherit 47 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDInfo.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import java.util.Set; 4 | 5 | import io.quarkus.runtime.annotations.RecordableConstructor; 6 | 7 | public class CRDInfo { 8 | private final String crdName; 9 | private final String crdSpecVersion; 10 | private final String filePath; 11 | private final Set dependentClassNames; 12 | 13 | @RecordableConstructor // constructor needs to be recordable for the class to be passed around by Quarkus 14 | public CRDInfo(String crdName, String crdSpecVersion, String filePath, Set dependentClassNames) { 15 | this.crdName = crdName; 16 | this.crdSpecVersion = crdSpecVersion; 17 | this.filePath = filePath; 18 | this.dependentClassNames = dependentClassNames; 19 | } 20 | 21 | public String getCrdName() { 22 | return this.crdName; 23 | } 24 | 25 | public String getCrdSpecVersion() { 26 | return this.crdSpecVersion; 27 | } 28 | 29 | public String getFilePath() { 30 | return this.filePath; 31 | } 32 | 33 | public Set getDependentClassNames() { 34 | return this.dependentClassNames; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return crdName; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/devui/commands/VersionsCommand.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment.devui.commands; 2 | 3 | import org.aesh.command.Command; 4 | import org.aesh.command.CommandDefinition; 5 | import org.aesh.command.CommandResult; 6 | import org.aesh.command.invocation.CommandInvocation; 7 | 8 | import io.quarkiverse.operatorsdk.runtime.Version; 9 | 10 | @CommandDefinition(name = "versions", description = "Outputs QOSDK, Quarkus, Fabric8 and JOSDK versions, with which this QOSDK version was built") 11 | @SuppressWarnings("rawtypes") 12 | public class VersionsCommand implements Command { 13 | 14 | private final Version version; 15 | 16 | public VersionsCommand(Version version) { 17 | this.version = version; 18 | } 19 | 20 | @Override 21 | public CommandResult execute(CommandInvocation commandInvocation) { 22 | String output = "Targeted Quarkus version: " + version.getQuarkusVersion() + "\n" + 23 | "Fabric8 client version targeted by JOSDK: " + version.getKubernetesClientVersion() + "\n" + 24 | "QOSDK version: " + version.getExtensionCompleteVersion() + "\n" + 25 | "JOSDK version: " + version.getSdkVersion(); 26 | commandInvocation.println(output); 27 | return CommandResult.SUCCESS; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common-deployment/src/main/java/io/quarkiverse/operatorsdk/common/AnnotationConfigurableAugmentedClassInfo.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.common; 2 | 3 | import java.util.Map; 4 | 5 | import org.jboss.jandex.ClassInfo; 6 | import org.jboss.jandex.DotName; 7 | import org.jboss.jandex.IndexView; 8 | import org.jboss.logging.Logger; 9 | 10 | public class AnnotationConfigurableAugmentedClassInfo extends SelectiveAugmentedClassInfo { 11 | private DotName associatedConfigurationClass; 12 | 13 | public AnnotationConfigurableAugmentedClassInfo(ClassInfo classInfo) { 14 | super(classInfo, Constants.ANNOTATION_CONFIGURABLE, 1); 15 | } 16 | 17 | @Override 18 | protected void doAugment(IndexView index, Logger log, Map context) { 19 | // record associated configuration annotation 20 | associatedConfigurationClass = typeAt(0).name(); 21 | 22 | // register associated class for reflection: this class is the annotation target class 23 | registerForReflection(classInfo().name().toString()); 24 | } 25 | 26 | public DotName getAssociatedConfigurationClass() { 27 | return associatedConfigurationClass; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return classInfo().name().toString() + " -> " + associatedConfigurationClass.toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/ConfiguredReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import io.javaoperatorsdk.operator.api.config.informer.Informer; 6 | import io.javaoperatorsdk.operator.api.reconciler.Context; 7 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 8 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 9 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 10 | import io.javaoperatorsdk.operator.processing.event.rate.RateLimited; 11 | import io.javaoperatorsdk.operator.processing.retry.GradualRetry; 12 | 13 | @ControllerConfiguration(name = ConfiguredReconciler.NAME, informer = @Informer(namespaces = "foo")) 14 | @GradualRetry(maxAttempts = ConfiguredReconciler.MAX_ATTEMPTS, initialInterval = 1000) 15 | @RateLimited(maxReconciliations = 1, within = 1, unit = TimeUnit.MINUTES) 16 | public class ConfiguredReconciler implements Reconciler { 17 | 18 | public static final String NAME = "annotation"; 19 | public static final int MAX_ATTEMPTS = 23; 20 | 21 | @Override 22 | public UpdateControl reconcile(ChildTestResource2 childTestResource2, 23 | Context context) throws Exception { 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/devconsole/EventSourceInfo.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime.devconsole; 2 | 3 | import java.util.Optional; 4 | 5 | import io.fabric8.kubernetes.api.model.HasMetadata; 6 | import io.javaoperatorsdk.operator.processing.event.source.Configurable; 7 | import io.javaoperatorsdk.operator.processing.event.source.EventSource; 8 | 9 | public class EventSourceInfo implements Comparable { 10 | private final EventSource metadata; 11 | 12 | public EventSourceInfo(EventSource metadata) { 13 | this.metadata = metadata; 14 | } 15 | 16 | public String getName() { 17 | return metadata.name(); 18 | } 19 | 20 | @SuppressWarnings("unused") 21 | public String getResourceClass() { 22 | return metadata.resourceType().getName(); 23 | } 24 | 25 | public String getType() { 26 | return metadata.getClass().getName(); 27 | } 28 | 29 | public Optional getConfiguration() { 30 | return metadata instanceof Configurable ? Optional.of(((Configurable) metadata).configuration()) 31 | : Optional.empty(); 32 | } 33 | 34 | @Override 35 | public int compareTo(EventSourceInfo other) { 36 | return getName().compareTo(other.getName()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/DefaultConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import jakarta.inject.Inject; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.RegisterExtension; 9 | 10 | import io.javaoperatorsdk.operator.api.config.ConfigurationService; 11 | import io.javaoperatorsdk.operator.api.reconciler.Constants; 12 | import io.quarkiverse.operatorsdk.test.sources.*; 13 | import io.quarkus.test.QuarkusUnitTest; 14 | 15 | public class DefaultConfigurationTest { 16 | 17 | @RegisterExtension 18 | static QuarkusUnitTest runner = new QuarkusUnitTest() 19 | .overrideConfigKey("quarkus.operator-sdk.start-operator", "false") 20 | .withApplicationRoot( 21 | jar -> jar.addClasses(SimpleReconciler.class, SimpleCR.class, SimpleStatus.class, SimpleSpec.class)); 22 | 23 | @Inject 24 | ConfigurationService configurationService; 25 | 26 | @Inject 27 | SimpleReconciler testReconciler; 28 | 29 | @Test 30 | void checkDefaultOperatorLevelNamespaces() { 31 | final var config = configurationService.getConfigurationFor(testReconciler); 32 | assertEquals(Constants.DEFAULT_NAMESPACES_SET, config.getInformerConfig().getNamespaces()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/ReflectionRegistrations.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import java.util.function.Predicate; 4 | 5 | import org.jboss.jandex.DotName; 6 | 7 | import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem; 8 | 9 | public class ReflectionRegistrations { 10 | 11 | private static final String[] PACKAGES_IGNORED_FOR_REFLECTION = { 12 | // Vert.x 13 | "io.vertx.core.", 14 | // OkHttp3 15 | "okhttp3.", 16 | // Okio 17 | "okio." 18 | }; 19 | 20 | static final IgnoreTypeForReflectionPredicate IGNORE_TYPE_FOR_REFLECTION_PREDICATE = new IgnoreTypeForReflectionPredicate(); 21 | 22 | private static class IgnoreTypeForReflectionPredicate implements Predicate { 23 | 24 | @Override 25 | public boolean test(DotName dotName) { 26 | if (ReflectiveHierarchyBuildItem.DefaultIgnoreTypePredicate.INSTANCE.test(dotName)) { 27 | return true; 28 | } 29 | String name = dotName.toString(); 30 | for (String packageName : PACKAGES_IGNORED_FOR_REFLECTION) { 31 | if (name.startsWith(packageName)) { 32 | return true; 33 | } 34 | } 35 | return false; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/pingpong/src/main/java/io/quarkiverse/operatorsdk/samples/pingpong/Ping.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Red Hat, Inc. and/or its affiliates. 3 | * 4 | *

5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | */ 15 | package io.quarkiverse.operatorsdk.samples.pingpong; 16 | 17 | import com.fasterxml.jackson.annotation.JsonInclude; 18 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 19 | 20 | import io.fabric8.kubernetes.api.model.Namespaced; 21 | import io.fabric8.kubernetes.client.CustomResource; 22 | import io.fabric8.kubernetes.model.annotation.Group; 23 | import io.fabric8.kubernetes.model.annotation.Version; 24 | 25 | @Group("samples.javaoperatorsdk.io") 26 | @Version("v1alpha1") 27 | @JsonInclude(Include.NON_NULL) 28 | public class Ping extends CustomResource implements Namespaced { 29 | 30 | public Ping() { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /samples/pingpong/src/main/java/io/quarkiverse/operatorsdk/samples/pingpong/Pong.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Red Hat, Inc. and/or its affiliates. 3 | * 4 | *

5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | */ 15 | package io.quarkiverse.operatorsdk.samples.pingpong; 16 | 17 | import com.fasterxml.jackson.annotation.JsonInclude; 18 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 19 | 20 | import io.fabric8.kubernetes.api.model.Namespaced; 21 | import io.fabric8.kubernetes.client.CustomResource; 22 | import io.fabric8.kubernetes.model.annotation.Group; 23 | import io.fabric8.kubernetes.model.annotation.Version; 24 | 25 | @Group("samples.javaoperatorsdk.io") 26 | @Version("v1alpha1") 27 | @JsonInclude(Include.NON_NULL) 28 | public class Pong extends CustomResource implements Namespaced { 29 | 30 | public Pong() { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /integration-tests/basic/src/test/java/io/quarkiverse/operatorsdk/it/EmptyOperatorLevelNamespacesTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.equalTo; 5 | import static org.hamcrest.CoreMatchers.is; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import io.quarkus.test.junit.QuarkusTest; 10 | import io.quarkus.test.junit.TestProfile; 11 | 12 | @QuarkusTest 13 | @TestProfile(EmptyOperatorLevelNamespacesTestProfile.class) 14 | public class EmptyOperatorLevelNamespacesTest { 15 | 16 | @Test 17 | public void reconcilersWithoutSpecificNamespacesShouldWatchAllNamespaces() { 18 | given() 19 | .when().get("/operator/" + TestReconciler.NAME + 20 | "/config") 21 | .then() 22 | .statusCode(200) 23 | .body("watchAllNamespaces", is(true)); 24 | } 25 | 26 | @Test 27 | public void reconcilerWithSpecificNamespacesShouldUseThem() { 28 | given() 29 | .when().get("/operator/" + ApplicationScopedReconciler.NAME + 30 | "/config") 31 | .then() 32 | .statusCode(200) 33 | .body("watchAllNamespaces", is(false)) 34 | .body("namespaces.size()", is(1)) 35 | .body("namespaces[0]", equalTo("default")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/OneNSConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.util.Set; 6 | 7 | import jakarta.inject.Inject; 8 | 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.RegisterExtension; 11 | 12 | import io.javaoperatorsdk.operator.api.config.ConfigurationService; 13 | import io.quarkiverse.operatorsdk.test.sources.OneNSReconciler; 14 | import io.quarkiverse.operatorsdk.test.sources.TestCR; 15 | import io.quarkus.test.QuarkusUnitTest; 16 | 17 | public class OneNSConfigurationTest { 18 | 19 | @RegisterExtension 20 | static QuarkusUnitTest runner = new QuarkusUnitTest() 21 | .overrideConfigKey("quarkus.operator-sdk.start-operator", "false") 22 | .overrideConfigKey("quarkus.http.test-port", "0") 23 | .withApplicationRoot( 24 | jar -> jar.addClasses(OneNSReconciler.class, TestCR.class)); 25 | 26 | @Inject 27 | ConfigurationService configurationService; 28 | 29 | @Inject 30 | OneNSReconciler testReconciler; 31 | 32 | @Test 33 | void checkDefaultOperatorLevelNamespaces() { 34 | final var config = configurationService.getConfigurationFor(testReconciler); 35 | assertEquals(Set.of(OneNSReconciler.NS), config.getInformerConfig().getNamespaces()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/DependentDefiningReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import io.fabric8.kubernetes.api.model.ConfigMap; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 6 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 7 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 8 | import io.javaoperatorsdk.operator.api.reconciler.Workflow; 9 | import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; 10 | 11 | // Note that this reconciler implementation and its dependents are not meant to be realistic but 12 | // rather exercise some of the features 13 | @Workflow(dependents = { 14 | @Dependent(type = ReadOnlyDependentResource.class, name = ReadOnlyDependentResource.NAME, readyPostcondition = ReadOnlyDependentResource.ReadOnlyReadyCondition.class), 15 | @Dependent(type = CRUDDependentResource.class, name = "crud", dependsOn = "read-only") 16 | }) 17 | @ControllerConfiguration(name = DependentDefiningReconciler.NAME) 18 | public class DependentDefiningReconciler implements Reconciler { 19 | 20 | public static final String NAME = "dependent"; 21 | 22 | @Override 23 | public UpdateControl reconcile(ConfigMap configMap, Context context) { 24 | return UpdateControl.noUpdate(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/CustomRateLimiter.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.time.Duration; 4 | import java.util.Optional; 5 | 6 | import io.javaoperatorsdk.operator.api.config.AnnotationConfigurable; 7 | import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; 8 | 9 | @SuppressWarnings("rawtypes") 10 | public class CustomRateLimiter implements RateLimiter, AnnotationConfigurable { 11 | private int value; 12 | 13 | @Override 14 | public Optional isLimited(RateLimitState rateLimitState) { 15 | return Optional.empty(); 16 | } 17 | 18 | @Override 19 | public RateLimitState initState() { 20 | return null; 21 | } 22 | 23 | @Override 24 | public void initFrom(CustomRateConfiguration customRateConfiguration) { 25 | this.value = customRateConfiguration.value(); 26 | } 27 | 28 | @SuppressWarnings("unused") 29 | // make it visible for JSON serialization 30 | public int getValue() { 31 | return value; 32 | } 33 | 34 | /* 35 | * Needed to allow proper byte recording as custom implementation configuration is now done at build time, thus requiring 36 | * configuration results to be recorded to be replayed at runtime. 37 | */ 38 | @SuppressWarnings("unused") 39 | public void setValue(int value) { 40 | this.value = value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/devui/DevUIProcessor.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment.devui; 2 | 3 | import io.quarkiverse.operatorsdk.runtime.devui.JSONRPCService; 4 | import io.quarkus.deployment.IsDevelopment; 5 | import io.quarkus.deployment.annotations.BuildStep; 6 | import io.quarkus.devui.spi.JsonRPCProvidersBuildItem; 7 | import io.quarkus.devui.spi.page.CardPageBuildItem; 8 | import io.quarkus.devui.spi.page.Page; 9 | 10 | public class DevUIProcessor { 11 | 12 | @SuppressWarnings("unused") 13 | @BuildStep(onlyIf = IsDevelopment.class) 14 | CardPageBuildItem create() { 15 | final var card = new CardPageBuildItem(); 16 | card.addLibraryVersion("io.javaoperatorsdk", "operator-framework-core", "Java Operator SDK", 17 | "https://github.com/operator-framework/java-operator-sdk"); 18 | card.setLogo("logo_dark.svg", "logo_light.svg"); 19 | card.addPage(Page.webComponentPageBuilder() 20 | .title("Controllers") 21 | .componentLink("qwc-qosdk-controllers.js") 22 | .dynamicLabelJsonRPCMethodName("controllersCount") 23 | .icon("font-awesome-solid:brain")); 24 | return card; 25 | } 26 | 27 | @SuppressWarnings("unused") 28 | @BuildStep 29 | JsonRPCProvidersBuildItem createJsonRPCService() { 30 | return new JsonRPCProvidersBuildItem(JSONRPCService.class); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/NullItemStore.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.util.stream.Stream; 4 | 5 | import io.fabric8.kubernetes.api.model.ResourceQuota; 6 | import io.fabric8.kubernetes.client.informers.cache.ItemStore; 7 | import io.quarkus.runtime.annotations.RegisterForReflection; 8 | 9 | @RegisterForReflection // for proper serialization in native mode 10 | public class NullItemStore implements ItemStore { 11 | 12 | public static final String NAME = "NullItemStoreName"; 13 | 14 | // so that it appears in the JSON configuration and we can check against it 15 | public String getName() { 16 | return NAME; 17 | } 18 | 19 | @Override 20 | public String getKey(ResourceQuota resourceQuota) { 21 | return null; 22 | } 23 | 24 | @Override 25 | public ResourceQuota put(String s, ResourceQuota resourceQuota) { 26 | return null; 27 | } 28 | 29 | @Override 30 | public ResourceQuota remove(String s) { 31 | return null; 32 | } 33 | 34 | @Override 35 | public Stream keySet() { 36 | return null; 37 | } 38 | 39 | @Override 40 | public Stream values() { 41 | return null; 42 | } 43 | 44 | @Override 45 | public int size() { 46 | return 0; 47 | } 48 | 49 | @Override 50 | public ResourceQuota get(String s) { 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/api/ConfigurableReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime.api; 2 | 3 | import io.fabric8.kubernetes.api.model.HasMetadata; 4 | import io.javaoperatorsdk.operator.api.config.ConfigurationService; 5 | import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider; 6 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 7 | 8 | /** 9 | * Implement to change a {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler}'s 10 | * configuration at runtime 11 | * 12 | * @param

the primary resource type of the reconciler 13 | * @since 7.2.0 14 | */ 15 | public interface ConfigurableReconciler

extends Reconciler

{ 16 | /** 17 | * Updates the reconciler's configuration by applying the modifications specified by the provided 18 | * {@link ControllerConfigurationOverrider}. Note that the resulting configuration update won't be recorded by the 19 | * {@link ConfigurationService} as this currently is a JOSDK limitation. To access the up-to-date configuration, you need to 20 | * retrieve it from the associated {@link io.javaoperatorsdk.operator.RegisteredController} from 21 | * {@link io.javaoperatorsdk.operator.RuntimeInfo}. 22 | * 23 | * @param configOverrider provides the modifications to apply to the existing reconciler's 24 | * configuration 25 | */ 26 | void updateConfigurationFrom(ControllerConfigurationOverrider

configOverrider); 27 | } 28 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/TestReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | import io.javaoperatorsdk.operator.api.reconciler.Workflow; 8 | import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; 9 | import io.quarkiverse.operatorsdk.annotations.RBACRule; 10 | import io.quarkiverse.operatorsdk.annotations.RBACVerbs; 11 | 12 | @Workflow(dependents = { 13 | @Dependent(type = CRUDConfigMap.class), 14 | @Dependent(type = ReadOnlySecret.class), 15 | @Dependent(type = CreateOnlyService.class), 16 | @Dependent(type = NonKubeResource.class), 17 | @Dependent(type = TypelessKubeResource.class), 18 | @Dependent(type = TypelessAnotherKubeResource.class), 19 | @Dependent(type = NoDefaultArgConstructorDependent.class) 20 | }) 21 | @ControllerConfiguration(name = TestReconciler.NAME) 22 | @RBACRule(verbs = RBACVerbs.UPDATE, apiGroups = RBACRule.ALL, resources = RBACRule.ALL) 23 | public class TestReconciler implements Reconciler { 24 | 25 | public static final String NAME = "test"; 26 | 27 | @Override 28 | public UpdateControl reconcile(TestCR testCR, Context context) { 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/exposedapp/src/main/java/io/halkyon/ServiceDependent.java: -------------------------------------------------------------------------------- 1 | package io.halkyon; 2 | 3 | import static io.halkyon.ExposedAppReconciler.LABELS_CONTEXT_KEY; 4 | import static io.halkyon.ExposedAppReconciler.createMetadata; 5 | 6 | import java.util.Map; 7 | 8 | import io.fabric8.kubernetes.api.model.Service; 9 | import io.fabric8.kubernetes.api.model.ServiceBuilder; 10 | import io.javaoperatorsdk.operator.api.reconciler.Context; 11 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; 12 | 13 | public class ServiceDependent extends CRUDKubernetesDependentResource { 14 | 15 | // todo: automatically generate 16 | public ServiceDependent() { 17 | super(Service.class); 18 | } 19 | 20 | @Override 21 | @SuppressWarnings("unchecked") 22 | public Service desired(ExposedApp exposedApp, Context context) { 23 | final var labels = (Map) context.managedWorkflowAndDependentResourceContext() 24 | .getMandatory(LABELS_CONTEXT_KEY, Map.class); 25 | 26 | return new ServiceBuilder() 27 | .withMetadata(createMetadata(exposedApp, labels)) 28 | .withNewSpec() 29 | .addNewPort() 30 | .withName("http") 31 | .withPort(8080) 32 | .withNewTargetPort().withValue(8080).endTargetPort() 33 | .endPort() 34 | .withSelector(labels) 35 | .withType("ClusterIP") 36 | .endSpec() 37 | .build(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/deployment/src/main/java/io/quarkiverse/operatorsdk/deployment/GeneratedKubernetesManifestsProcessor.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.deployment; 2 | 3 | import java.util.List; 4 | import java.util.function.BooleanSupplier; 5 | 6 | import org.eclipse.microprofile.config.ConfigProvider; 7 | 8 | import io.quarkiverse.operatorsdk.common.DeserializedKubernetesResourcesBuildItem; 9 | import io.quarkiverse.operatorsdk.common.GeneratedResourcesUtils; 10 | import io.quarkus.deployment.annotations.BuildStep; 11 | import io.quarkus.kubernetes.spi.GeneratedKubernetesResourceBuildItem; 12 | 13 | public class GeneratedKubernetesManifestsProcessor { 14 | private static class NeedResourcesDeserialization implements BooleanSupplier { 15 | @Override 16 | public boolean getAsBoolean() { 17 | final var helmEnabled = ConfigProvider.getConfig() 18 | .getOptionalValue("quarkus.operator-sdk.helm.enabled", Boolean.class).orElse(false); 19 | final var bundleEnabled = ConfigProvider.getConfig() 20 | .getOptionalValue("quarkus.operator-sdk.bundle.enabled", Boolean.class).orElse(false); 21 | return helmEnabled || bundleEnabled; 22 | } 23 | } 24 | 25 | @BuildStep(onlyIf = NeedResourcesDeserialization.class) 26 | DeserializedKubernetesResourcesBuildItem deserializeGeneratedKubernetesResources( 27 | List generatedResources) { 28 | return new DeserializedKubernetesResourcesBuildItem(GeneratedResourcesUtils.loadFrom(generatedResources)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /samples/pingpong/src/test/java/io/quarkiverse/operatorsdk/samples/pingpong/AssertGeneratedResourcesIT.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.pingpong; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.fail; 5 | 6 | import java.io.FileInputStream; 7 | import java.util.List; 8 | 9 | import org.junit.jupiter.api.Test; 10 | 11 | import io.fabric8.kubernetes.client.KubernetesClientBuilder; 12 | import io.fabric8.openshift.api.model.operatorhub.v1alpha1.ClusterServiceVersion; 13 | import io.fabric8.openshift.api.model.operatorhub.v1alpha1.StrategyDeploymentPermissions; 14 | 15 | class AssertGeneratedResourcesIT { 16 | 17 | @Test 18 | void verifyPingPongClusterServiceVersion() { 19 | try (final var client = new KubernetesClientBuilder().build()) { 20 | final var csv = client.getKubernetesSerialization().unmarshal(new FileInputStream( 21 | "target/bundle/pingpong-operator/manifests/pingpong-operator.clusterserviceversion.yaml"), 22 | ClusterServiceVersion.class); 23 | // should have only one cluster rule permission 24 | List clusterPermissions = csv.getSpec().getInstall().getSpec() 25 | .getClusterPermissions(); 26 | assertEquals(1, clusterPermissions.size()); 27 | List permissions = csv.getSpec().getInstall().getSpec().getPermissions(); 28 | assertEquals(1, permissions.size()); 29 | } catch (Exception e) { 30 | fail(e); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /samples/joke/src/main/java/io/quarkiverse/operatorsdk/samples/joke/JokeService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Red Hat, Inc. and/or its affiliates. 3 | * 4 | *

5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | */ 15 | package io.quarkiverse.operatorsdk.samples.joke; 16 | 17 | import jakarta.enterprise.context.ApplicationScoped; 18 | import jakarta.ws.rs.DefaultValue; 19 | import jakarta.ws.rs.GET; 20 | import jakarta.ws.rs.Path; 21 | import jakarta.ws.rs.Produces; 22 | 23 | import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; 24 | import org.jboss.resteasy.reactive.RestPath; 25 | import org.jboss.resteasy.reactive.RestQuery; 26 | 27 | import io.quarkiverse.operatorsdk.samples.joke.JokeRequestSpec.Category; 28 | 29 | @ApplicationScoped 30 | @RegisterRestClient(configKey = "joke-api") 31 | public interface JokeService { 32 | 33 | @GET 34 | @Path("/{category}/any") 35 | @Produces("application/json") 36 | JokeModel getRandom(@RestPath Category category, @RestQuery(value = "blacklistFlags") String excluded, 37 | @RestQuery(value = "safe-mode") boolean safe, @DefaultValue("single") @RestQuery String type); 38 | } 39 | -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/TestReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Cleaner; 4 | import io.javaoperatorsdk.operator.api.reconciler.Context; 5 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 6 | import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; 7 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 8 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 9 | import io.javaoperatorsdk.operator.api.reconciler.Workflow; 10 | import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; 11 | 12 | @Workflow(dependents = { 13 | @Dependent(name = TestReconciler.DEPLOYMENT, type = DeploymentDependent.class, activationCondition = CustomActionvationCondition.class, readyPostcondition = ReadyPostCondition.class, reconcilePrecondition = ReconcilePrecondition.class), 14 | @Dependent(type = ConfigMapDependent.class, deletePostcondition = DeletePostCondition.class, dependsOn = TestReconciler.DEPLOYMENT) 15 | }) 16 | @ControllerConfiguration 17 | public class TestReconciler implements Reconciler, Cleaner { 18 | 19 | public static final String DEPLOYMENT = "deployment"; 20 | 21 | @Override 22 | public UpdateControl reconcile(TestResource resource, Context context) throws Exception { 23 | return UpdateControl.noUpdate(); 24 | } 25 | 26 | @Override 27 | public DeleteControl cleanup(TestResource resource, Context context) throws Exception { 28 | return DeleteControl.defaultDelete(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/QuarkusKubernetesDependentResourceConfig.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import io.fabric8.kubernetes.api.model.HasMetadata; 4 | import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; 5 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; 6 | import io.quarkus.runtime.annotations.RecordableConstructor; 7 | 8 | public class QuarkusKubernetesDependentResourceConfig extends KubernetesDependentResourceConfig { 9 | 10 | @RecordableConstructor 11 | public QuarkusKubernetesDependentResourceConfig(Boolean useSSA, boolean createResourceOnlyIfNotExistingWithSSA, 12 | QuarkusInformerConfiguration informerConfig) { 13 | super(useSSA, createResourceOnlyIfNotExistingWithSSA, informerConfig); 14 | } 15 | 16 | // Getter required for Quarkus' RecordableConstructor, must match the associated constructor parameter name 17 | @SuppressWarnings("unused") 18 | public boolean isCreateResourceOnlyIfNotExistingWithSSA() { 19 | return createResourceOnlyIfNotExistingWithSSA(); 20 | } 21 | 22 | // Getter required for Quarkus' RecordableConstructor, must match the associated constructor parameter name 23 | @SuppressWarnings("unused") 24 | public Boolean isUseSSA() { 25 | return useSSA(); 26 | } 27 | 28 | // Getter required for Quarkus' RecordableConstructor, must match the associated constructor parameter name 29 | @SuppressWarnings("unused") 30 | public InformerConfiguration getInformerConfig() { 31 | return informerConfig(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /samples/mysql-schema/src/main/java/io/quarkiverse/operatorsdk/samples/mysqlschema/schema/Schema.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.samples.mysqlschema.schema; 2 | 3 | import java.io.Serializable; 4 | import java.util.Objects; 5 | 6 | import io.javaoperatorsdk.operator.processing.ResourceIDProvider; 7 | 8 | public class Schema implements Serializable, ResourceIDProvider { 9 | 10 | private final String name; 11 | private final String characterSet; 12 | 13 | public Schema(String name, String characterSet) { 14 | this.name = name; 15 | this.characterSet = characterSet; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public String getCharacterSet() { 23 | return characterSet; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) 29 | return true; 30 | if (o == null || getClass() != o.getClass()) 31 | return false; 32 | Schema schema = (Schema) o; 33 | return Objects.equals(name, schema.name) && Objects.equals(characterSet, schema.characterSet); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return Objects.hash(name, characterSet); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "Schema{" + "name='" + name + '\'' + ", characterSet='" + characterSet + "'}"; 44 | } 45 | 46 | @Override 47 | public String resourceId() { 48 | // in this case, we use the naming mechanism as the resource identifying mechanism for ResourceIDProvider 49 | // so we use the implementation provided by getName 50 | return getName(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/deploy-with-helm.adoc: -------------------------------------------------------------------------------- 1 | = Deploying your operator with Helm 2 | 3 | https://helm.sh/[Helm] is a package manager for Kubernetes that allows you to define, 4 | install, and upgrade Kubernetes applications. Quarkus Operator SDK integrates with the 5 | https://github.com/quarkiverse/quarkus-helm[Quarkus Helm] Quarkiverse extension to 6 | generate Helm charts for your operators. 7 | 8 | == Usage 9 | 10 | If you include the `quarkus-helm` extension in your project, the Quarkus Operator SDK will 11 | automatically detect it and include the generated CRDs in the generated Helm chart. 12 | 13 | [source,xml,subs=attributes+] 14 | ---- 15 | 16 | io.quarkiverse.helm 17 | quarkus-helm 18 | ${version.io.quarkiverse.helm} 19 | 20 | ---- 21 | 22 | No further configuration is needed. You can find more information about the Helm extension 23 | in its documentation available at https://docs.quarkiverse.io/quarkus-helm/dev/index.html or at its GitHub repository https://github.com/quarkiverse/quarkus-helm. 24 | 25 | NOTE: The generated files layout has changed: they have moved from the `helm` directory to the `helm/kubernetes/` directory. While the layout and files have changed, the functionality should be now better as the `quarkus-helm` extension is much more full-featured than the previous Helm support used by QOSDK. The values should also be pretty self-explanatory, the only thing of note being that the watched namespaces are now set via the `app.envs.QUARKUS_OPERATOR_SDK_CONTROLLERS__NAMESPACES` value (i.e. you're now setting the env variable that is used to configure the namespaces at runtime in the operator's `Deployment`). 26 | -------------------------------------------------------------------------------- /samples/joke/src/main/java/io/quarkiverse/operatorsdk/samples/joke/JokeModel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Red Hat, Inc. and/or its affiliates. 3 | * 4 | *

5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | */ 15 | package io.quarkiverse.operatorsdk.samples.joke; 16 | 17 | import java.util.Map; 18 | 19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 20 | 21 | @JsonIgnoreProperties(ignoreUnknown = true) 22 | public class JokeModel { 23 | /* 24 | * "error": true, 25 | * "internalError": false, 26 | * "code": 106, 27 | * "message": "No matching joke found", 28 | * "causedBy": [ 29 | * "No jokes were found that match your provided filter(s)." 30 | * ], 31 | * "additionalInfo": "The specified ID range is invalid. Got: \"foo\" but max possible ID range is: \"0-303\".", 32 | * "timestamp": 1615998352457 33 | */ 34 | 35 | public boolean error; 36 | public boolean internalError; 37 | public String message; 38 | public String[] causedBy; 39 | public String additionalInfo; 40 | public String category; 41 | public String joke; 42 | public Map flags; 43 | public int id; 44 | public boolean safe; 45 | public String lang; 46 | } 47 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/UnownedTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.RegisterExtension; 9 | 10 | import io.fabric8.kubernetes.client.CustomResource; 11 | import io.quarkiverse.operatorsdk.test.sources.*; 12 | import io.quarkus.test.ProdBuildResults; 13 | import io.quarkus.test.ProdModeTestResults; 14 | import io.quarkus.test.QuarkusProdModeTest; 15 | 16 | public class UnownedTest { 17 | // Start unit test with your extension loaded 18 | @RegisterExtension 19 | static final QuarkusProdModeTest config = new QuarkusProdModeTest() 20 | .setApplicationName(UnownedReconciler.NAME) 21 | .withApplicationRoot( 22 | (jar) -> jar.addClasses(UnownedReconciler.class, TestCR.class)) 23 | .overrideConfigKey("quarkus.operator-sdk.crd.generate-all", "true") 24 | .overrideConfigKey("quarkus.operator-sdk.controllers." + UnownedReconciler.NAME + ".unowned-primary", "true"); 25 | 26 | @ProdBuildResults 27 | private ProdModeTestResults prodModeTestResults; 28 | 29 | @Test 30 | public void shouldNotGenerateUnownedPrimary() throws IOException { 31 | final var kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes"); 32 | final var kubeManifest = kubernetesDir.resolve("kubernetes.yml"); 33 | Assertions.assertTrue(Files.exists(kubeManifest)); 34 | 35 | // checks that no CRD has been generated 36 | Assertions.assertFalse(Files.exists(kubernetesDir.resolve(CustomResource.getCRDName(TestCR.class) + "-v1.yml"))); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/NoCRDGenerationTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.RegisterExtension; 9 | 10 | import io.fabric8.kubernetes.client.CustomResource; 11 | import io.quarkiverse.operatorsdk.test.sources.*; 12 | import io.quarkus.test.ProdBuildResults; 13 | import io.quarkus.test.ProdModeTestResults; 14 | import io.quarkus.test.QuarkusProdModeTest; 15 | 16 | public class NoCRDGenerationTest { 17 | 18 | // Start unit test with your extension loaded 19 | @RegisterExtension 20 | static final QuarkusProdModeTest config = new QuarkusProdModeTest() 21 | .setApplicationName("test") 22 | .withApplicationRoot( 23 | (jar) -> jar.addClasses(SimpleReconciler.class, SimpleCR.class, SimpleSpec.class, SimpleStatus.class)) 24 | .overrideConfigKey("quarkus.operator-sdk.crd.generate", "false"); 25 | 26 | @ProdBuildResults 27 | private ProdModeTestResults prodModeTestResults; 28 | 29 | @Test 30 | public void shouldNotGenerateCRDs() throws IOException { 31 | final var kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes"); 32 | final var kubeManifest = kubernetesDir.resolve("kubernetes.yml"); 33 | Assertions.assertTrue(Files.exists(kubeManifest)); 34 | 35 | // checks that CRDs are NOT generated 36 | Assertions.assertFalse(Files.exists(kubernetesDir.resolve(CustomResource.getCRDName(TestCR.class) + "-v1.yml"))); 37 | Assertions.assertFalse(Files.exists(kubernetesDir.resolve(CustomResource.getCRDName(SimpleCR.class) + "-v1.yml"))); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #quarkus.operator-sdk.namespaces=operator-level 2 | quarkus.operator-sdk.generation-aware=false 3 | # this shouldn't impact anything for the tests since we're not checking manifest generation here 4 | quarkus.operator-sdk.generate-with-watched-namespaces=operator-level-for-manifests 5 | 6 | 7 | quarkus.operator-sdk.controllers.test.retry.max-attempts=1 8 | # generating manifests with specific namespaces shouldn't preclude using operator-level namespaces at runtime 9 | quarkus.operator-sdk.controllers.test.generate-with-watched-namespaces=builtime-namespace1, buildtime-ns2 10 | quarkus.operator-sdk.controllers.annotation.finalizer=from-property/finalizer 11 | quarkus.operator-sdk.controllers.annotation.namespaces=bar 12 | quarkus.operator-sdk.controllers.annotation.retry.interval.initial=20000 13 | quarkus.operator-sdk.controllers.annotation.selector=environment=production,tier!=frontend 14 | quarkus.operator-sdk.controllers.ApplicationScoped.namespaces=default 15 | quarkus.operator-sdk.controllers.variablens.namespaces=${VARIABLE_NS_ENV} 16 | quarkus.operator-sdk.controllers.name\ with\ space.namespaces=name-with-space 17 | quarkus.operator-sdk.controllers.secret.max-reconciliation-interval=PT15M 18 | 19 | quarkus.operator-sdk.concurrent-reconciliation-threads=10 20 | quarkus.operator-sdk.termination-timeout-seconds=20 21 | quarkus.operator-sdk.crd.validate=false 22 | quarkus.operator-sdk.crd.versions=v1beta1 23 | quarkus.operator-sdk.activate-leader-election-for-profiles=prod,test,dev 24 | 25 | ## activate to prevent the operator to start when debugging tests (note that some tests might fail because of this) 26 | quarkus.operator-sdk.start-operator=false 27 | quarkus.operator-sdk.enable-ssa=false 28 | 29 | ## randomize Quarkus port for tests 30 | quarkus.http.test-port=0 31 | -------------------------------------------------------------------------------- /bundle-generator/runtime/src/main/java/io/quarkiverse/operatorsdk/bundle/runtime/BundleGenerationConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.runtime; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | 7 | import io.quarkus.runtime.annotations.ConfigRoot; 8 | import io.smallrye.config.ConfigMapping; 9 | import io.smallrye.config.WithDefault; 10 | 11 | @ConfigMapping(prefix = "quarkus.operator-sdk.bundle") 12 | @ConfigRoot 13 | public interface BundleGenerationConfiguration { 14 | String DEFAULT_BUNDLE_NAME = "QOSDK_DEFAULT"; 15 | 16 | /** 17 | * Whether the extension should generate the Operator bundle. 18 | */ 19 | @WithDefault("true") 20 | Boolean enabled(); 21 | 22 | /** 23 | * The list of channels that bundle belongs to. By default, it's "alpha". 24 | */ 25 | @WithDefault("alpha") 26 | List channels(); 27 | 28 | /** 29 | * The default channel for the bundle. 30 | */ 31 | Optional defaultChannel(); 32 | 33 | /** 34 | * The replaces value that should be used in the generated CSV. 35 | */ 36 | Optional replaces(); 37 | 38 | /** 39 | * The version value that should be used in the generated CSV instead of the automatically detected one extracted from the 40 | * project information. 41 | */ 42 | Optional version(); 43 | 44 | /** 45 | * Per-bundle configuration. Note that you can also provide default values that will be applied to all your bundles by 46 | * adding configuration using the {@link #DEFAULT_BUNDLE_NAME} key. In that case, any configuration found under that key 47 | * will be used as default for every bundle unless otherwise overridden. 48 | * 49 | * @since 6.8.0 50 | */ 51 | Map bundles(); 52 | } 53 | -------------------------------------------------------------------------------- /core/deployment/src/test/java/io/quarkiverse/operatorsdk/test/sources/SimpleReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | import io.quarkiverse.operatorsdk.annotations.RBACRoleRef; 8 | import io.quarkiverse.operatorsdk.annotations.RBACRule; 9 | import io.quarkiverse.operatorsdk.annotations.RBACVerbs; 10 | 11 | @ControllerConfiguration(name = SimpleReconciler.NAME) 12 | @RBACRule(verbs = RBACVerbs.UPDATE, apiGroups = SimpleReconciler.CERTIFICATES_K8S_IO_GROUP, resources = SimpleReconciler.ADDITIONAL_UPDATE_RESOURCE) 13 | @RBACRule(verbs = SimpleReconciler.SIGNERS_VERB, apiGroups = SimpleReconciler.CERTIFICATES_K8S_IO_GROUP, resources = SimpleReconciler.SIGNERS_RESOURCE, resourceNames = SimpleReconciler.SIGNERS_RESOURCE_NAMES) 14 | @RBACRoleRef(name = SimpleReconciler.ROLE_REF_NAME, kind = RBACRoleRef.RoleKind.ClusterRole) 15 | public class SimpleReconciler implements Reconciler { 16 | 17 | public static final String NAME = "simple"; 18 | public static final String CERTIFICATES_K8S_IO_GROUP = "certificates.k8s.io"; 19 | public static final String ADDITIONAL_UPDATE_RESOURCE = "certificatesigningrequests/approval"; 20 | public static final String SIGNERS_VERB = "approve"; 21 | public static final String SIGNERS_RESOURCE = "signers"; 22 | public static final String SIGNERS_RESOURCE_NAMES = "kubernetes.io/kubelet-serving"; 23 | public static final String ROLE_REF_NAME = "system:auth-delegator"; 24 | 25 | @Override 26 | public UpdateControl reconcile(SimpleCR simpleCR, Context context) { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/builders/CustomResourceManifestsBuilder.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle.deployment.builders; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.util.List; 8 | 9 | import io.fabric8.kubernetes.api.model.ServiceAccount; 10 | import io.fabric8.kubernetes.api.model.apps.Deployment; 11 | import io.fabric8.kubernetes.api.model.rbac.ClusterRole; 12 | import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; 13 | import io.fabric8.kubernetes.api.model.rbac.Role; 14 | import io.fabric8.kubernetes.api.model.rbac.RoleBinding; 15 | import io.quarkiverse.operatorsdk.bundle.runtime.CSVMetadataHolder; 16 | import io.quarkiverse.operatorsdk.runtime.CRDInfo; 17 | 18 | public class CustomResourceManifestsBuilder extends ManifestsBuilder { 19 | 20 | private static final String MANIFESTS = "manifests"; 21 | 22 | private final CRDInfo crd; 23 | 24 | public CustomResourceManifestsBuilder(CSVMetadataHolder metadata, CRDInfo crd) { 25 | super(metadata); 26 | 27 | this.crd = crd; 28 | } 29 | 30 | @Override 31 | public Path getFileName() { 32 | return Path.of(MANIFESTS, crd.getCrdName() + "-" + crd.getCrdSpecVersion() + ".crd.yml"); 33 | } 34 | 35 | @Override 36 | public byte[] getManifestData(List serviceAccounts, List clusterRoleBindings, 37 | List clusterRoles, List roleBindings, List roles, List deployments) 38 | throws IOException { 39 | return Files.readAllBytes(new File(crd.getFilePath()).toPath()); 40 | } 41 | 42 | @Override 43 | public String getManifestType() { 44 | return "Custom Resource Definition"; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /common-deployment/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | io.quarkiverse.operatorsdk 5 | quarkus-operator-sdk-build-parent 6 | 7.4.1-SNAPSHOT 7 | ../build-parent/pom.xml 8 | 9 | 4.0.0 10 | 11 | Quarkus - Operator SDK - Common - Deployment 12 | quarkus-operator-sdk-common-deployment 13 | 14 | 15 | 16 | org.jboss.logging 17 | jboss-logging 18 | 19 | 20 | io.javaoperatorsdk 21 | operator-framework-core 22 | 23 | 24 | io.quarkiverse.operatorsdk 25 | quarkus-operator-sdk-common 26 | 27 | 28 | io.quarkus 29 | quarkus-core-deployment 30 | 31 | 32 | io.quarkus 33 | quarkus-kubernetes-client-deployment 34 | 35 | 36 | io.quarkus 37 | quarkus-junit5-internal 38 | test 39 | 40 | 41 | io.quarkiverse.operatorsdk 42 | quarkus-operator-sdk-annotations 43 | compile 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /core/deployment/src/main/resources/helm/additional-crd-role-binding-template.yaml: -------------------------------------------------------------------------------- 1 | {{ if eq $.Values.watchNamespaces "JOSDK_WATCH_CURRENT" }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: RoleBinding 4 | metadata: 5 | name: {role-binding-name} 6 | namespace: {{ $.Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ .Chart.Name }} 9 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 10 | app.kubernetes.io/managed-by: quarkus 11 | roleRef: 12 | kind: {role-ref-kind} 13 | apiGroup: {role-ref-api-group} 14 | name: {role-ref-name} 15 | subjects: 16 | - kind: ServiceAccount 17 | name: {{ $.Chart.Name }} 18 | {{ else if eq $.Values.watchNamespaces "JOSDK_ALL_NAMESPACES" }} 19 | apiVersion: rbac.authorization.k8s.io/v1 20 | kind: ClusterRoleBinding 21 | metadata: 22 | name: {role-binding-name} 23 | labels: 24 | app.kubernetes.io/name: {{ .Chart.Name }} 25 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 26 | app.kubernetes.io/managed-by: quarkus 27 | roleRef: 28 | kind: {role-ref-kind} 29 | apiGroup: {role-ref-api-group} 30 | name: {role-ref-name} 31 | subjects: 32 | - kind: ServiceAccount 33 | name: {{ $.Chart.Name }} 34 | namespace: {{ $.Release.Namespace }} 35 | {{ else }} 36 | {{ range $anamespace := ( split "," $.Values.watchNamespaces ) }} 37 | apiVersion: rbac.authorization.k8s.io/v1 38 | kind: RoleBinding 39 | metadata: 40 | name: {role-binding-name} 41 | namespace: {{ $anamespace }} 42 | labels: 43 | app.kubernetes.io/name: {{ .Chart.Name }} 44 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 45 | app.kubernetes.io/managed-by: quarkus 46 | roleRef: 47 | kind: {role-ref-kind} 48 | apiGroup: {role-ref-api-group} 49 | name: {role-ref-name} 50 | subjects: 51 | - kind: ServiceAccount 52 | name: {{ $.Chart.Name }} 53 | namespace: {{ $.Release.Namespace }} 54 | --- 55 | {{- end }} 56 | {{- end }} 57 | -------------------------------------------------------------------------------- /cli/src/main/java/io/quarkiverse/operatorsdk/cli/API.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.cli; 2 | 3 | import static io.quarkiverse.operatorsdk.common.CLIConstants.*; 4 | 5 | import java.util.concurrent.Callable; 6 | 7 | import org.jboss.logging.Logger; 8 | 9 | import io.quarkiverse.operatorsdk.common.Files; 10 | import picocli.CommandLine; 11 | 12 | @CommandLine.Command(name = "api", mixinStandardHelpOptions = true, description = API_DESCRIPTION) 13 | public class API implements Callable { 14 | private static final Logger log = Logger.getLogger(API.class); 15 | private static final String SHORT_PREFIX = "-"; 16 | private static final String OPTION_PREFIX = "--"; 17 | @CommandLine.Option(names = { SHORT_PREFIX + API_KIND_SHORT, 18 | OPTION_PREFIX + API_KIND }, description = API_KIND_DESCRIPTION, required = true) 19 | private String kind; 20 | 21 | @CommandLine.Option(names = { SHORT_PREFIX + API_GROUP_SHORT, 22 | OPTION_PREFIX + API_GROUP }, description = API_GROUP_DESCRIPTION, required = true) 23 | private String group; 24 | 25 | @CommandLine.Option(names = { SHORT_PREFIX + API_VERSION_SHORT, 26 | OPTION_PREFIX + API_VERSION }, description = API_VERSION_DESCRIPTION, required = true) 27 | private String version; 28 | 29 | private static final Files.MessageWriter writer = new Files.MessageWriter() { 30 | @Override 31 | public void write(String message, Exception e, boolean forError) { 32 | if (forError) { 33 | log.error(message, e); 34 | } else { 35 | log.info(message); 36 | } 37 | } 38 | }; 39 | 40 | @Override 41 | public Integer call() throws Exception { 42 | return Files.generateAPIFiles(group, version, kind, writer) ? CommandLine.ExitCode.OK 43 | : CommandLine.ExitCode.SOFTWARE; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/OutputLabelSelectorsIfRequestedTest.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.bundle; 2 | 3 | import static io.quarkiverse.operatorsdk.bundle.Utils.BUNDLE; 4 | import static io.quarkiverse.operatorsdk.bundle.Utils.getCSVFor; 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | import static org.junit.jupiter.api.Assertions.assertNotNull; 7 | 8 | import java.io.IOException; 9 | 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.RegisterExtension; 12 | 13 | import io.quarkiverse.operatorsdk.bundle.sources.First; 14 | import io.quarkiverse.operatorsdk.bundle.sources.ReconcilerWithNoCsvMetadata; 15 | import io.quarkus.test.ProdBuildResults; 16 | import io.quarkus.test.ProdModeTestResults; 17 | import io.quarkus.test.QuarkusProdModeTest; 18 | 19 | public class OutputLabelSelectorsIfRequestedTest { 20 | @RegisterExtension 21 | static final QuarkusProdModeTest config = new QuarkusProdModeTest() 22 | .setApplicationName("output-kube-labels") 23 | .overrideConfigKey("quarkus.kubernetes.add-version-to-label-selectors", "true") 24 | .withApplicationRoot((jar) -> jar 25 | .addClasses(First.class, ReconcilerWithNoCsvMetadata.class)); 26 | 27 | @ProdBuildResults 28 | private ProdModeTestResults prodModeTestResults; 29 | 30 | @Test 31 | public void shouldHaveVersionLabelWhenRequested() throws IOException { 32 | final var name = "output-kube-labels"; 33 | final var csv = getCSVFor(prodModeTestResults.getBuildDir().resolve(BUNDLE), name); 34 | final var deployment = csv.getSpec().getInstall().getSpec().getDeployments().get(0); 35 | assertEquals(name, deployment.getName()); 36 | assertNotNull(deployment.getSpec().getSelector().getMatchLabels().get("app.kubernetes.io/version")); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /helm/deployment/src/test/java/io/quarkiverse/operatorsdk/helm/deployment/test/sources/SimpleReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.helm.deployment.test.sources; 2 | 3 | import io.javaoperatorsdk.operator.api.reconciler.Context; 4 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 5 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 6 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 7 | import io.quarkiverse.operatorsdk.annotations.RBACRoleRef; 8 | import io.quarkiverse.operatorsdk.annotations.RBACRule; 9 | import io.quarkiverse.operatorsdk.annotations.RBACVerbs; 10 | 11 | @ControllerConfiguration(name = SimpleReconciler.NAME) 12 | @RBACRule(verbs = RBACVerbs.UPDATE, apiGroups = SimpleReconciler.CERTIFICATES_K8S_IO_GROUP, resources = SimpleReconciler.ADDITIONAL_UPDATE_RESOURCE) 13 | @RBACRule(verbs = SimpleReconciler.SIGNERS_VERB, apiGroups = SimpleReconciler.CERTIFICATES_K8S_IO_GROUP, resources = SimpleReconciler.SIGNERS_RESOURCE, resourceNames = SimpleReconciler.SIGNERS_RESOURCE_NAMES) 14 | @RBACRoleRef(name = SimpleReconciler.ROLE_REF_NAME, kind = RBACRoleRef.RoleKind.ClusterRole) 15 | public class SimpleReconciler implements Reconciler { 16 | 17 | public static final String NAME = "simple"; 18 | public static final String CERTIFICATES_K8S_IO_GROUP = "certificates.k8s.io"; 19 | public static final String ADDITIONAL_UPDATE_RESOURCE = "certificatesigningrequests/approval"; 20 | public static final String SIGNERS_VERB = "approve"; 21 | public static final String SIGNERS_RESOURCE = "signers"; 22 | public static final String SIGNERS_RESOURCE_NAMES = "kubernetes.io/kubelet-serving"; 23 | public static final String ROLE_REF_NAME = "system:auth-delegator"; 24 | 25 | @Override 26 | public UpdateControl reconcile(SimpleCR simpleCR, Context context) { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /integration-tests/basic/src/main/java/io/quarkiverse/operatorsdk/it/SecretReconciler.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it; 2 | 3 | import java.util.Base64; 4 | import java.util.HashMap; 5 | 6 | import io.fabric8.kubernetes.api.model.Secret; 7 | import io.javaoperatorsdk.operator.api.reconciler.Context; 8 | import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; 9 | import io.javaoperatorsdk.operator.api.reconciler.MaxReconciliationInterval; 10 | import io.javaoperatorsdk.operator.api.reconciler.Reconciler; 11 | import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; 12 | 13 | @ControllerConfiguration(name = SecretReconciler.NAME, maxReconciliationInterval = @MaxReconciliationInterval(interval = 2)) 14 | public class SecretReconciler implements Reconciler { 15 | public static final String NAME = "secret"; 16 | 17 | @Override 18 | public UpdateControl reconcile(Secret secret, Context context) { 19 | if (secret.getType().equals("Opaque")) { 20 | final var labels = secret.getMetadata().getLabels(); 21 | if (labels != null && "true".equals(labels.get("quarkus-operator-sdk.secret-reconciler-marker"))) { 22 | System.out.println("Reconciling secret " + secret.getMetadata().getName()); 23 | 24 | var data = secret.getData(); 25 | if (data == null) { 26 | data = new HashMap<>(); 27 | secret.setStringData(data); 28 | } 29 | final String foo = data.putIfAbsent("quarkus-operator-sdk.added-value", 30 | Base64.getEncoder().encodeToString("quarkus-operator-sdk rocks!".getBytes())); 31 | if (foo == null) { 32 | return UpdateControl.patchResource(secret); 33 | } 34 | } 35 | } 36 | return UpdateControl.noUpdate(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDGenerationInfo.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import io.quarkus.runtime.annotations.IgnoreProperty; 7 | import io.quarkus.runtime.annotations.RecordableConstructor; 8 | 9 | public class CRDGenerationInfo { 10 | private final boolean applyCRDs; 11 | private final boolean validateCRDs; 12 | private final CRDInfos crds; 13 | private final Set generated; 14 | 15 | @RecordableConstructor // constructor needs to be recordable for the class to be passed around by Quarkus 16 | public CRDGenerationInfo(boolean applyCRDs, boolean validateCRDs, CRDInfos crds, 17 | Set generated) { 18 | this.applyCRDs = applyCRDs; 19 | this.validateCRDs = validateCRDs; 20 | this.crds = crds; 21 | this.generated = generated; 22 | } 23 | 24 | // Getter required for Quarkus' RecordableConstructor, must match the associated constructor parameter name 25 | public CRDInfos getCrds() { 26 | return crds; 27 | } 28 | 29 | // Getter required for Quarkus' RecordableConstructor, must match the associated constructor parameter name 30 | public Set getGenerated() { 31 | return generated; 32 | } 33 | 34 | // Getter required for Quarkus' RecordableConstructor, must match the associated constructor parameter name 35 | public boolean isApplyCRDs() { 36 | return applyCRDs; 37 | } 38 | 39 | @IgnoreProperty 40 | public Map getCRDInfosFor(String crdName) { 41 | return crds.getOrCreateCRDSpecVersionToInfoMapping(crdName); 42 | } 43 | 44 | // Getter required for Quarkus' RecordableConstructor, must match the associated constructor parameter name 45 | public boolean isValidateCRDs() { 46 | return validateCRDs; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/OperatorHealthCheck.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.runtime; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | import jakarta.inject.Inject; 5 | 6 | import org.eclipse.microprofile.health.HealthCheck; 7 | import org.eclipse.microprofile.health.HealthCheckResponse; 8 | import org.eclipse.microprofile.health.Readiness; 9 | 10 | import io.javaoperatorsdk.operator.Operator; 11 | 12 | @Readiness 13 | @ApplicationScoped 14 | public class OperatorHealthCheck implements HealthCheck { 15 | 16 | public static final String HEALTH_CHECK_NAME = "Quarkus Operator SDK health check"; 17 | public static final String OK = "OK"; 18 | @Inject 19 | Operator operator; 20 | 21 | @Override 22 | public HealthCheckResponse call() { 23 | final var runtimeInfo = operator.getRuntimeInfo(); 24 | if (runtimeInfo.isStarted()) { 25 | final var response = HealthCheckResponse.named(HEALTH_CHECK_NAME); 26 | final boolean[] healthy = { true }; 27 | runtimeInfo.getRegisteredControllers().forEach(rc -> { 28 | final var name = rc.getConfiguration().getName(); 29 | final var unhealthy = rc.getControllerHealthInfo().unhealthyEventSources(); 30 | if (unhealthy.isEmpty()) { 31 | response.withData(name, OK); 32 | } else { 33 | healthy[0] = false; 34 | response 35 | .withData(name, "unhealthy: " + String.join(", ", unhealthy.keySet())); 36 | } 37 | }); 38 | if (healthy[0]) { 39 | response.up(); 40 | } else { 41 | response.down(); 42 | } 43 | return response.build(); 44 | } 45 | return HealthCheckResponse.down(HEALTH_CHECK_NAME); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # IDE Config and Code Style 2 | 3 | This project has a strictly enforced code style. Code formatting is done by the Eclipse code 4 | formatter, using the config files found in the `contributing` directory. By 5 | default when you run `mvn install` the code will be formatted automatically. When submitting a 6 | pull request the CI build will fail if running the formatter results in any code changes, so it is 7 | recommended that you always run a full Maven build before submitting a pull request. 8 | 9 | If you want to run the formatting without doing a full build, you can run `mvn process-sources`. 10 | 11 | #### Eclipse Setup 12 | 13 | Open the *Preferences* window, and then navigate to _Java_ -> _Code Style_ -> _Formatter_. Click _ 14 | Import_ and then select the `eclipse-format.xml` file in the `contrib` 15 | directory. 16 | 17 | Next navigate to _Java_ -> _Code Style_ -> _Organize Imports_. Click _Import_ and select 18 | the `eclipse.importorder` file. 19 | 20 | #### IDEA Setup 21 | 22 | Open the _Preferences_ window (or _Settings_ depending on your edition), navigate to _Plugins_ and 23 | install 24 | the [Eclipse Code Formatter Plugin](https://plugins.jetbrains.com/plugin/6546-eclipse-code-formatter) 25 | from the Marketplace. 26 | 27 | Restart your IDE, open the *Preferences* (or *Settings*) window again and navigate to _Other 28 | Settings_ -> _Eclipse Code Formatter_. 29 | 30 | Select _Use the Eclipse Code Formatter_, then change the _Eclipse Java Formatter Config File_ to 31 | point to the 32 | `eclipse-format.xml` file in the `contributing` directory. Make sure the _ 33 | Optimize Imports_ box is ticked, and select the `eclipse.importorder` file as the import order 34 | config file. 35 | 36 | Next, disable wildcard imports: 37 | navigate to _Editor_ -> _Code Style_ -> _Java_ -> _Imports_ 38 | and set _Class count to use import with '\*'_ to `999`. Do the same with _Names count to use static 39 | import with '\*'_. -------------------------------------------------------------------------------- /integration-tests/cdi/src/main/java/io/quarkiverse/operatorsdk/it/cdi/DeploymentDependent.java: -------------------------------------------------------------------------------- 1 | package io.quarkiverse.operatorsdk.it.cdi; 2 | 3 | import java.util.Map; 4 | 5 | import org.jboss.logging.Logger; 6 | 7 | import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; 8 | import io.fabric8.kubernetes.api.model.apps.Deployment; 9 | import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; 10 | import io.javaoperatorsdk.operator.api.reconciler.Context; 11 | import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; 12 | 13 | public class DeploymentDependent extends CRUDNoGCKubernetesDependentResource { 14 | 15 | private static final Logger LOG = Logger.getLogger(DeploymentDependent.class); 16 | 17 | @Override 18 | protected Deployment desired(TestResource primary, Context context) { 19 | return new DeploymentBuilder() 20 | .withMetadata(new ObjectMetaBuilder() 21 | .withName(primary.getMetadata().getName() + "-deployment") 22 | .withNamespace("default") 23 | .withLabels(Map.of("app", primary.getMetadata().getName())) 24 | .build()) 25 | .withNewSpec() 26 | .withNewSelector().withMatchLabels(Map.of("app", primary.getMetadata().getName())).endSelector() 27 | .withNewTemplate().withNewMetadata().withLabels(Map.of("app", primary.getMetadata().getName())).endMetadata() 28 | .withNewSpec().addNewContainer() 29 | .withName("nginx").withImage("nginx:1.14.2") 30 | .addNewPort().withName("http").withProtocol("TCP").withContainerPort(8080).endPort() 31 | .endContainer() 32 | .endSpec() 33 | .endTemplate() 34 | .endSpec() 35 | .build(); 36 | } 37 | } 38 | --------------------------------------------------------------------------------