From 6e553c980346b835d3088589eb859c5e0660a0f1 Mon Sep 17 00:00:00 2001 From: yashsharma127 Date: Fri, 2 Aug 2024 23:35:17 +0530 Subject: [PATCH] Resolved Errors+issues,Repo Restructuring, Updated readme+architecture files and added status update feature --- PHEE-operator/ARCHITECTURE.md | 166 ++++++- PHEE-operator/README.md | 54 +- .../{ => cr}/ph-ee-importer-rdbms-cr.yaml | 6 +- .../deploy/crds/ph-ee-importer-rdbms-crd.yaml | 35 +- PHEE-operator/deploy/operator-deployment.yaml | 67 --- PHEE-operator/deploy/operator/operator.yaml | 135 +++++ .../main/java/com/example/OperatorMain.java | 30 +- .../java/com/example/PhEeImporterRdbms.java | 8 - .../example/PhEeImporterRdbmsController.java | 461 ++++++++++-------- .../com/example/PhEeImporterRdbmsSpec.java | 216 -------- .../customresource/PhEeImporterRdbms.java | 15 + .../customresource/PhEeImporterRdbmsSpec.java | 372 ++++++++++++++ .../PhEeImporterRdbmsStatus.java | 78 +++ .../java/com/example/utils/LoggingUtil.java | 66 +++ .../com/example/utils/StatusUpdateUtil.java | 39 ++ 15 files changed, 1207 insertions(+), 541 deletions(-) rename PHEE-operator/deploy/{ => cr}/ph-ee-importer-rdbms-cr.yaml (89%) delete mode 100644 PHEE-operator/deploy/operator-deployment.yaml create mode 100644 PHEE-operator/deploy/operator/operator.yaml delete mode 100644 PHEE-operator/src/main/java/com/example/PhEeImporterRdbms.java delete mode 100644 PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsSpec.java create mode 100644 PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbms.java create mode 100644 PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsSpec.java create mode 100644 PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsStatus.java create mode 100644 PHEE-operator/src/main/java/com/example/utils/LoggingUtil.java create mode 100644 PHEE-operator/src/main/java/com/example/utils/StatusUpdateUtil.java diff --git a/PHEE-operator/ARCHITECTURE.md b/PHEE-operator/ARCHITECTURE.md index 33dd264..ca09889 100644 --- a/PHEE-operator/ARCHITECTURE.md +++ b/PHEE-operator/ARCHITECTURE.md @@ -1,43 +1,167 @@ -# Architecture -This file contain the repo structure and information on each component operator include. +# PHEE Operator Architecture + +This document provides an in-depth overview of the architecture of the PHEE Operator, detailing the design decisions, components, and their interactions. ## Repo Structure +``` PHEE-operator/ ├── deploy/ +│ ├── cr/ +│ │ └── ph-ee-importer-rdbms-cr.yaml │ ├── crds/ -│ │ └── ph-ee-importer-rdbms-crd.yaml -│ ├── operator-deployment.yaml -│ └── ph-ee-importer-rdbms-cr.yaml +│ │ └── ph-ee-importer-rdbms-crd.yaml +│ └── operator/ +│ └── operator.yaml ├── src/ │ └── main/ │ └── java/ │ └── com/ │ └── example/ -│ └── operator/ -│ ├── PhEeImporterRdbms.java -│ ├── PhEeImporterRdbmsController.java -│ ├── PhEeImporterRdbmsSpec.java -│ └── OperatorMain.java -├── pom.xml -└── README.md +│ ├── customresource/ +│ │ ├── PhEeImporterRdbms.java +│ │ ├── PhEeImporterRdbmsSpec.java +│ │ └── PhEeImporterRdbmsStatus.java +│ ├── utils/ +| │ ├── LoggingUtil.java +| │ └── StatusUpdateUtil.java +│ ├── OperatorMain.java +| └── PhEeImporterRdbmsController.java +└── pom.xml +``` + +## Table of Contents + +1. [Introduction](#introduction) +2. [Overview](#overview) +3. [Components](#components) + - [Custom Resource Definition (CRD)](#custom-resource-definition-crd) + - [Custom Resource (CR)](#custom-resource-cr) + - [Operator](#operator) + - [Controller](#controller) + - [Utility Classes](#utility-classes) +4. [Deployment](#deployment) +5. [Design Decisions](#design-decisions) + +## Introduction + +The PHEE Operator is designed to manage and automate the lifecycle of a Custom Resource within a Kubernetes cluster. This document outlines the key components and architectural decisions that shape the operator. + +## Overview + +The PHEE Operator comprises several key components: +- Custom Resource Definitions (CRDs) that specify the schema for custom resources. +- Custom Resources (CRs) that represent instances of the CRD. +- The Operator which contains the logic for managing CRs. +- The Controller which handles reconciliation loops to ensure the desired state of the cluster. +- Utility classes that aid in logging and status updates. + + +## Components + +### Custom Resource Definition (CRD) + +- **File**: `deploy/crds/ph-ee-importer-rdbms-crd.yaml` + +- **Purpose**: This file contains the Custom Resource Definition (CRD) for the operator. The CRD defines the schema for the Custom Resources that will be managed by the operator. + +- **Details**: + - Specifies fields such as `spec`, `status`, etc. + - Defines validation criteria for each field. + +### Custom Resource (CR) + +- **File**: `deploy/cr/ph-ee-importer-rdbms-cr.yaml` + +- **Purpose**: This file contains the Custom Resource (CR) script for the Importer RDBMS, which includes values for the fields defined in the CRD. + +- **Details**: + - Contains fields defined in the CRD. + - Specifies configuration parameters for the Importer RDBMS. + +### Operator + +- **Main File**: `src/main/java/com/example/operator/OperatorMain.java` + +- **Purpose**: This is the main file for the operator. It registers the operator controller and starts the operator to apply the reconciliation logic. + +- **Details**: + - Initializes the Kubernetes client. + - Registers custom resource schemas and controllers. + +### Controller + +- **File**: `src/main/java/com/example/operator/PhEeImporterRdbmsController.java` + +- **Purpose**: This file is the controller for the operator, containing the reconciliation logic for the Importer RDBMS deployment and its RBAC configurations. It uses the CR to create Kubernetes resource objects. + +- **Details**: + - Watches for changes to custom resources. + - Applies necessary changes to the cluster to match the desired state. + - Handles error states and retries. + +### Custom Resource Classes + +#### PhEeImporterRdbms.java + +- **File**: `src/main/java/com/example/customresource/PhEeImporterRdbms.java` + +- **Purpose**: Defines the Custom Resource class according to the specification used in the controller file. + +#### PhEeImporterRdbmsSpec.java + +- **File**: `src/main/java/com/example/customresource/PhEeImporterRdbmsSpec.java` + +- **Purpose**: Defines the specification for the operator, containing fields defined in the CRD and applied by the CRs. + +#### PhEeImporterRdbmsStatus.java + +- **File**: `src/main/java/com/example/customresource/PhEeImporterRdbmsStatus.java` + +- **Purpose**: Defines the status fields for the Custom Resource, allowing the operator to communicate the current state of the resource. + +### Utility Classes + +#### LoggingUtil.java + +- **File**: `src/main/java/com/example/utils/LoggingUtil.java` + +- **Purpose**: Provides utility methods for logging within the operator. + +#### StatusUpdateUtil.java + +- **File**: `src/main/java/com/example/utils/StatusUpdateUtil.java` + +- **Purpose**: Provides utility methods for updating the status of the Custom Resource. + +### pom.xml + +- **File**: `pom.xml` -**ph-ee-importer-rdbms-crd.yaml:** This file contains the Custom Resource Definition (CRD) for the operator. The CRD defines the schema for the Custom Resources that will be managed by the operator. +- **Purpose**: This file contains all the dependencies required for this project. -**operator-deployment.yaml:** This file contains the deployment script for the operator itself, including its required Role-Based Access Control (RBAC) configurations. +## Deployment -**ph-ee-importer-rdbms-cr.yaml:** This file contains the Custom Resource (CR) script for the Importer RDBMS, which includes values for the fields defined in the CRD. +The deployment of the PHEE Operator involves several steps: -**OperatorMain.java:** This is the main file for the operator. It registers the operator controller and starts the operator to apply the reconciliation logic. +- **CRD Deployment**: Apply the CRD to the cluster. +- **Operator Deployment**: Deploy the operator using a Deployment resource. +- **CR Deployment**: Create custom resources as needed. -**PhEeImporterRdbmsController.java:** This file is the controller for the operator, containing the reconciliation logic for the Importer RDBMS deployment and its RBAC configurations. It uses the CR to create Kubernetes resource objects. +Deployment files: -**PhEeImporterRdbmsSpec.java:** This file defines the specification for the operator, containing fields defined in the CRD and applied by the CRs. +- **CRD**: `deploy/crds/ph-ee-importer-rdbms-crd.yaml` +- **Operator Deployment**: `deploy/operator/operator.yaml` +- **Custom Resource**: `deploy/cr/ph-ee-importer-rdbms-cr.yaml` -**PhEeImporterRdbms.java:** This file defines the Custom Resource class according to the specification used in the controller file. +## Design Decisions -**pom.xml:** This file contains all the dependencies required for this project. +- **Language Choice**: The operator is implemented in Java due to its strong typing and extensive ecosystem. +- **Framework**: Utilized the Java Operator SDK for streamlined development. +- **CRD Structure**: Designed to be extensible and easy to validate. +- **Controller Logic**: Focused on idempotency and robustness. + -## Note +### Note This file is still in progress will be updated as the project progresses. \ No newline at end of file diff --git a/PHEE-operator/README.md b/PHEE-operator/README.md index d5f7a1c..03aa331 100644 --- a/PHEE-operator/README.md +++ b/PHEE-operator/README.md @@ -1,26 +1,46 @@ # Local Setup - -Prerequisite -- Kubernetes cluster setup -- maven, jdk, kubectl and docker installed -**Install the maven dependencies** -`mvn clean install` +This guide provides steps to set up the PHEE Operator in a k3s cluster. The current setup does not contain a Dockerfile. We use the Maven Jib plugin to build a Docker image, save that image to a .tar file, load it into the k3s cluster, and then apply the CRD, Operator, and CR. -**Creating local docker image using maven jib** -`mvn compile jib:dockerBuild -Dimage=ph-ee-importer-rdbms-operator:latest` +## Prerequisites -**Save the Docker image to a tar file** -`docker save ph-ee-importer-rdbms-operator:latest -o ph-ee-importer-rdbms-operator.tar` +- k3s cluster setup +- Maven, JDK, kubectl, and Docker installed -**Load the tar file into k3s** -`sudo k3s ctr images import ph-ee-importer-rdbms-operator.tar` +## Steps -**Apply CRD, Operator and then Custom Resource** -`kubectl apply -f deploy/crds/ph-ee-importer-rdbms-crd.yaml` -`kubectl apply -f deploy/operator-deployment.yaml` -`kubectl apply -f deploy/ph-ee-importer-rdbms-cr.yaml` +### 1. Install the Maven dependencies +``` +mvn clean install +``` -## Note +### 2. Creating local docker image using maven jib + +``` +mvn compile jib:dockerBuild -Dimage=ph-ee-importer-rdbms-operator:latest +``` + +### 3. Save the Docker image to a tar file + +``` +docker save ph-ee-importer-rdbms-operator:latest -o ph-ee-importer-rdbms-operator.tar +``` + +### 4. Load the tar file into k3s + +``` +sudo k3s ctr images import ph-ee-importer-rdbms-operator.tar +``` + +### 5. Apply CRD, Operator and Custom Resource + +``` +kubectl apply -f deploy/crds/ph-ee-importer-rdbms-crd.yaml +kubectl apply -f deploy/operator/operator.yaml +kubectl apply -f deploy/cr/ph-ee-importer-rdbms-cr.yaml +``` + +### Note This file is still in progress will be updated as the project progresses. +Also, currently operator is configured for deploying importer-rdbms only diff --git a/PHEE-operator/deploy/ph-ee-importer-rdbms-cr.yaml b/PHEE-operator/deploy/cr/ph-ee-importer-rdbms-cr.yaml similarity index 89% rename from PHEE-operator/deploy/ph-ee-importer-rdbms-cr.yaml rename to PHEE-operator/deploy/cr/ph-ee-importer-rdbms-cr.yaml index aabb156..a738364 100644 --- a/PHEE-operator/deploy/ph-ee-importer-rdbms-cr.yaml +++ b/PHEE-operator/deploy/cr/ph-ee-importer-rdbms-cr.yaml @@ -1,4 +1,4 @@ -apiVersion: example.com/v1 +apiVersion: my.custom.group/v1 kind: PhEeImporterRdbms metadata: name: sample-ph-ee-importer-rdbms @@ -19,9 +19,9 @@ spec: resources: limits: cpu: 500m - memory: 512M + memory: 512Mi requests: cpu: 100m - memory: 256M + memory: 256Mi javaToolOptions: "-Xmx256M" bucketName: paymenthub-ee-dev diff --git a/PHEE-operator/deploy/crds/ph-ee-importer-rdbms-crd.yaml b/PHEE-operator/deploy/crds/ph-ee-importer-rdbms-crd.yaml index 860b1d6..dcac830 100644 --- a/PHEE-operator/deploy/crds/ph-ee-importer-rdbms-crd.yaml +++ b/PHEE-operator/deploy/crds/ph-ee-importer-rdbms-crd.yaml @@ -1,13 +1,23 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: ph-ee-importer-rdbmses.example.com + name: pheeimporterrdbmses.my.custom.group # Use the current naming convention spec: - group: example.com + group: my.custom.group + names: + kind: PhEeImporterRdbms + listKind: PhEeImporterRdbmsList + plural: pheeimporterrdbmses + singular: pheeimporterrdbms + shortNames: # Add short names if required + - peeirdbms + scope: Namespaced versions: - - name: v1 + - name: v1 served: true storage: true + subresources: + status: {} # Ensure status is defined as a subresource schema: openAPIV3Schema: type: object @@ -62,11 +72,14 @@ spec: type: string bucketName: type: string - scope: Namespaced - names: - plural: ph-ee-importer-rdbmses - singular: ph-ee-importer-rdbms - kind: PhEeImporterRdbms - shortNames: - - peeirdbms - + status: + type: object + properties: + availableReplicas: + type: integer + errorMessage: + type: string + lastAppliedImage: + type: string + ready: + type: boolean diff --git a/PHEE-operator/deploy/operator-deployment.yaml b/PHEE-operator/deploy/operator-deployment.yaml deleted file mode 100644 index 04bccf4..0000000 --- a/PHEE-operator/deploy/operator-deployment.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ph-ee-importer-rdbms-operator - namespace: paymenthub -spec: - replicas: 1 - selector: - matchLabels: - app: ph-ee-importer-rdbms-operator - template: - metadata: - labels: - app: ph-ee-importer-rdbms-operator - spec: - serviceAccountName: ph-ee-importer-rdbms-operator - containers: - - name: ph-ee-importer-rdbms-operator - image: docker.io/openmf/ph-ee-importer-rdbms:v1.7.1 - imagePullPolicy: Always - env: - - name: KUBERNETES_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: ph-ee-importer-rdbms-operator - namespace: paymenthub ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: ph-ee-importer-rdbms-operator - namespace: paymenthub -rules: - - apiGroups: [""] - resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "configmaps", "secrets"] - verbs: ["*"] - - apiGroups: ["apps"] - resources: ["deployments", "daemonsets", "replicasets", "statefulsets"] - verbs: ["*"] - - apiGroups: ["rbac.authorization.k8s.io"] - resources: ["roles", "rolebindings"] - verbs: ["*"] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["*"] - - apiGroups: ["example.com"] - resources: ["ph-ee-importer-rdbmses"] - verbs: ["*"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: ph-ee-importer-rdbms-operator - namespace: paymenthub -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: ph-ee-importer-rdbms-operator -subjects: - - kind: ServiceAccount - name: ph-ee-importer-rdbms-operator - namespace: paymenthub diff --git a/PHEE-operator/deploy/operator/operator.yaml b/PHEE-operator/deploy/operator/operator.yaml new file mode 100644 index 0000000..0598bee --- /dev/null +++ b/PHEE-operator/deploy/operator/operator.yaml @@ -0,0 +1,135 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: phee-importer-operator-sa + namespace: default + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: phee-importer-operator + namespace: default + labels: + app: phee-importer-operator +spec: + replicas: 1 + selector: + matchLabels: + app: phee-importer-operator + template: + metadata: + labels: + app: phee-importer-operator + spec: + containers: + - name: operator + image: ph-ee-importer-rdbms-operator:latest # Use the locally built image + imagePullPolicy: IfNotPresent + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LOG_LEVEL + value: INFO # You can change this to DEBUG or another level as needed + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + serviceAccountName: phee-importer-operator-sa + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: phee-importer-operator-role +rules: +- apiGroups: + - "" + resources: + - deployments + - services + - serviceaccounts + - configmaps + - secrets + - pods + verbs: + - '*' +- apiGroups: + - "apps" + resources: + - deployments + - services + - configmaps + verbs: + - '*' +- apiGroups: + - "apiextensions.k8s.io" + resources: + - customresourcedefinitions + verbs: + - '*' +- apiGroups: + - "my.custom.group" + resources: + - pheeimporterrdbmses + - pheeimporterrdbmses/status + verbs: + - '*' +- apiGroups: + - "rbac.authorization.k8s.io" + resources: + - roles + - rolebindings + - clusterroles + - clusterrolebindings + verbs: + - '*' + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: phee-importer-operator-role-binding +subjects: + - kind: ServiceAccount + name: phee-importer-operator-sa + namespace: default +roleRef: + kind: ClusterRole + name: phee-importer-operator-role + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: phee-importer-operator-role + namespace: default +rules: +- apiGroups: + - my.custom.group + resources: + - pheeimporterrdbmses + - pheeimporterrdbmses/status + verbs: + - '*' + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: phee-importer-operator-role-binding + namespace: default +subjects: + - kind: ServiceAccount + name: phee-importer-operator-sa + namespace: default +roleRef: + kind: Role + name: phee-importer-operator-role + apiGroup: rbac.authorization.k8s.io diff --git a/PHEE-operator/src/main/java/com/example/OperatorMain.java b/PHEE-operator/src/main/java/com/example/OperatorMain.java index 7e3b970..4739ec9 100644 --- a/PHEE-operator/src/main/java/com/example/OperatorMain.java +++ b/PHEE-operator/src/main/java/com/example/OperatorMain.java @@ -1,18 +1,38 @@ package com.example; - + import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import com.example.operator.PhEeImporterRdbmsController; +import com.example.PhEeImporterRdbmsController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class OperatorMain { + private static final Logger log = LoggerFactory.getLogger(OperatorMain.class); + public static void main(String[] args) { - try (KubernetesClient client = new KubernetesClientBuilder().build()) { - Operator operator = new Operator(client, o -> o.withStopOnInformerErrorDuringStartup(false)); - Reconciler reconciler = new PhEeImporterRdbmsController(); + log.info("Starting the Payment Hub EE Operator!"); + + KubernetesClient client = new KubernetesClientBuilder().build(); // Moved outside try block + Operator operator = new Operator(client, o -> o.withStopOnInformerErrorDuringStartup(false)); + log.info("Operator instance created."); + + try { + Reconciler reconciler = new PhEeImporterRdbmsController(client); // Pass client to the controller operator.register(reconciler); + log.info("Reconciler {} registered.", reconciler.getClass().getSimpleName()); + operator.start(); + log.info("Operator started successfully."); + + // Keep the operator running indefinitely + Thread.currentThread().join(); + } catch (Exception e) { + log.error("Failed to start the operator due to an error: ", e); + } finally { + // Ensure the client is closed when the operator is stopped + client.close(); } } } diff --git a/PHEE-operator/src/main/java/com/example/PhEeImporterRdbms.java b/PHEE-operator/src/main/java/com/example/PhEeImporterRdbms.java deleted file mode 100644 index 9537c16..0000000 --- a/PHEE-operator/src/main/java/com/example/PhEeImporterRdbms.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.example; - -import io.fabric8.kubernetes.api.model.Namespaced; -import io.fabric8.kubernetes.client.CustomResource; -import com.example.operator.PhEeImporterRdbmsSpec; - -public class PhEeImporterRdbms extends CustomResource implements Namespaced { -} diff --git a/PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsController.java b/PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsController.java index 6d484a1..aebd26f 100644 --- a/PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsController.java +++ b/PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsController.java @@ -1,25 +1,30 @@ -package com.example.operator; - +package com.example; + +// Kubernetes API model imports import io.fabric8.kubernetes.api.model.*; +import io.fabric8.kubernetes.api.model.apps.*; import io.fabric8.kubernetes.api.model.rbac.*; + +// Kubernetes client imports import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.Resource; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.DeploymentSpec; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.SecretBuilder; + +// Operator SDK imports import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; -import java.util.concurrent.TimeUnit; -import com.example.PhEeImporterRdbms; + +// Logging imports import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.inject.Inject; +// Custom classes +import com.example.customresource.PhEeImporterRdbms; +import com.example.utils.LoggingUtil; +import com.example.utils.StatusUpdateUtil; + +// utils import java.util.*; @@ -28,35 +33,39 @@ public class PhEeImporterRdbmsController implements Reconciler reconcile(PhEeImporterRdbms resource, Context context) { - log.info("Reconciling PhEeImporterRdbms: {}", resource.getMetadata().getName()); + LoggingUtil.logResourceDetails(resource); + + try { - // calling all the reconciliation methods and providing resource as an argument - try { - reconcileDeployment(resource); - reconcileSecret(resource); - reconcileConfigMap(resource); reconcileServiceAccount(resource); + reconcileSecret(resource); reconcileClusterRole(resource); reconcileClusterRoleBinding(resource); reconcileRole(resource); - reconcileRoleBinding(resource); + reconcileRoleBinding(resource); + reconcileDeployment(resource); + + return StatusUpdateUtil.updateStatus(kubernetesClient, resource, resource.getSpec().getReplicas(), resource.getSpec().getImage(), true, ""); - return UpdateControl.noUpdate(); } catch (Exception e) { - log.error("Error during reconciliation", e); - return UpdateControl.noUpdate(); + LoggingUtil.logError("Error during reconciliation", e); + return StatusUpdateUtil.updateErrorStatus(kubernetesClient, resource, resource.getSpec().getImage(), e); } } - - //Below are all the resource reconciliation methods private void reconcileDeployment(PhEeImporterRdbms resource) { + log.info("Reconciling Deployment for resource: {}", resource.getMetadata().getName()); Deployment deployment = createDeployment(resource); + log.info("Created Deployment spec: {}", deployment); + Resource deploymentResource = kubernetesClient.apps().deployments() .inNamespace(resource.getMetadata().getNamespace()) .withName(resource.getMetadata().getName()); @@ -70,8 +79,109 @@ private void reconcileDeployment(PhEeImporterRdbms resource) { } } + private Deployment createDeployment(PhEeImporterRdbms resource) { + log.info("Creating Deployment spec for resource: {}", resource.getMetadata().getName()); + + // Define labels for the Deployment and Pod templates + Map labels = new HashMap<>(); + labels.put("app", resource.getMetadata().getName()); + labels.put("managed-by", "ph-ee-importer-operator"); // Optional label for identifying managed resources + + // Build the container with environment variables, resources, and volume mounts + Container container = new ContainerBuilder() + .withName(resource.getMetadata().getName()) + .withImage(resource.getSpec().getImage()) + .withEnv(createEnvironmentVariables(resource)) + .withResources(createResourceRequirements(resource)) + .withVolumeMounts(new VolumeMountBuilder() + .withName("ph-ee-config") + .withMountPath("/config") + .build()) + .build(); + + // Create PodSpec with the defined container and volumes + PodSpec podSpec = new PodSpecBuilder() + .withContainers(container) + .withVolumes(new VolumeBuilder() + .withName("ph-ee-config") + .withConfigMap(new ConfigMapVolumeSourceBuilder() + .withName("ph-ee-config") + .build()) + .build()) + .build(); + + // Build the PodTemplateSpec with metadata and spec + PodTemplateSpec podTemplateSpec = new PodTemplateSpecBuilder() + .withNewMetadata() + .withLabels(labels) + .endMetadata() + .withSpec(podSpec) + .build(); + + // Define the DeploymentSpec with replicas, selector, and template + DeploymentSpec deploymentSpec = new DeploymentSpecBuilder() + .withReplicas(resource.getSpec().getReplicas()) + .withSelector(new LabelSelectorBuilder() + .withMatchLabels(labels) + .build()) + .withTemplate(podTemplateSpec) + .build(); + + // Create Deployment metadata with owner references + ObjectMeta metadata = new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .withLabels(labels) + .withOwnerReferences(createOwnerReferences(resource)) + .build(); + + // Build the final Deployment object + return new DeploymentBuilder() + .withMetadata(metadata) + .withSpec(deploymentSpec) + .build(); + } + + // Helper method to create environment variables + private List createEnvironmentVariables(PhEeImporterRdbms resource) { + return Arrays.asList( + new EnvVar("SPRING_PROFILES_ACTIVE", resource.getSpec().getSpringProfilesActive(), null), + new EnvVar("DATASOURCE_CORE_USERNAME", resource.getSpec().getDatasource().getUsername(), null), + new EnvVar("DATASOURCE_CORE_PASSWORD", null, new EnvVarSourceBuilder().withNewSecretKeyRef("database-password", "importer-rdbms-secret", false).build()), + new EnvVar("DATASOURCE_CORE_HOST", resource.getSpec().getDatasource().getHost(), null), + new EnvVar("DATASOURCE_CORE_PORT", String.valueOf(resource.getSpec().getDatasource().getPort()), null), + new EnvVar("DATASOURCE_CORE_SCHEMA", resource.getSpec().getDatasource().getSchema(), null), + new EnvVar("LOGGING_LEVEL_ROOT", resource.getSpec().getLogging().getLevelRoot(), null), + new EnvVar("LOGGING_PATTERN_CONSOLE", resource.getSpec().getLogging().getPatternConsole(), null), + new EnvVar("JAVA_TOOL_OPTIONS", resource.getSpec().getJavaToolOptions(), null), + new EnvVar("APPLICATION_BUCKET_NAME", resource.getSpec().getBucketName(), null), + new EnvVar("CLOUD_AWS_REGION_STATIC", null, new EnvVarSourceBuilder().withNewSecretKeyRef("aws-region", "bulk-processor-secret", false).build()), + new EnvVar("AWS_ACCESS_KEY", null, new EnvVarSourceBuilder().withNewSecretKeyRef("aws-access-key", "bulk-processor-secret", false).build()), + new EnvVar("AWS_SECRET_KEY", null, new EnvVarSourceBuilder().withNewSecretKeyRef("aws-secret-key", "bulk-processor-secret", false).build()) + ); + } + + // Helper method to create resource requirements + private ResourceRequirements createResourceRequirements(PhEeImporterRdbms resource) { + return new ResourceRequirementsBuilder() + .withLimits(new HashMap() {{ + put("cpu", new Quantity(resource.getSpec().getResources().getLimits().getCpu())); + put("memory", new Quantity(resource.getSpec().getResources().getLimits().getMemory())); + }}) + .withRequests(new HashMap() {{ + put("cpu", new Quantity(resource.getSpec().getResources().getRequests().getCpu())); + put("memory", new Quantity(resource.getSpec().getResources().getRequests().getMemory())); + }}) + .build(); + } + + + private void reconcileSecret(PhEeImporterRdbms resource) { + log.info("Reconciling Secret for resource: {}", resource.getMetadata().getName()); Secret secret = createSecret(resource); + log.info("Created Secret spec: {}", secret); + Resource secretResource = kubernetesClient.secrets() .inNamespace(resource.getMetadata().getNamespace()) .withName("importer-rdbms-secret"); @@ -85,23 +195,24 @@ private void reconcileSecret(PhEeImporterRdbms resource) { } } - private void reconcileConfigMap(PhEeImporterRdbms resource) { - ConfigMap configMap = createConfigMap(resource); - Resource configMapResource = kubernetesClient.configMaps() - .inNamespace(resource.getMetadata().getNamespace()) - .withName("ph-ee-config"); - - if (configMapResource.get() == null) { - configMapResource.create(configMap); - log.info("Created new ConfigMap: {}", "ph-ee-config"); - } else { - configMapResource.patch(configMap); - log.info("Updated existing ConfigMap: {}", "ph-ee-config"); - } + private Secret createSecret(PhEeImporterRdbms resource) { + log.info("Creating Secret spec for resource: {}", resource.getMetadata().getName()); + return new SecretBuilder() + .withNewMetadata() + .withName("importer-rdbms-secret") + .withNamespace(resource.getMetadata().getNamespace()) + .withOwnerReferences(createOwnerReferences(resource)) + .endMetadata() + .addToData("database-password", Base64.getEncoder().encodeToString(resource.getSpec().getDatasource().getPassword().getBytes())) + .build(); } + private void reconcileServiceAccount(PhEeImporterRdbms resource) { + log.info("Reconciling ServiceAccount for resource: {}", resource.getMetadata().getName()); ServiceAccount serviceAccount = createServiceAccount(resource); + log.info("Created ServiceAccount spec: {}", serviceAccount); + Resource serviceAccountResource = kubernetesClient.serviceAccounts() .inNamespace(resource.getMetadata().getNamespace()) .withName("ph-ee-importer-rdbms"); @@ -115,30 +226,24 @@ private void reconcileServiceAccount(PhEeImporterRdbms resource) { } } - private void reconcileClusterRole(PhEeImporterRdbms resource) { - ClusterRole clusterRole = createClusterRole(resource); - Resource clusterRoleResource = kubernetesClient.rbac().clusterRoles() - .withName("ph-ee-importer-rdbms-c-role"); - - if (clusterRoleResource.get() == null) { - clusterRoleResource.create(clusterRole); - log.info("Created new ClusterRole: {}", "ph-ee-importer-rdbms-c-role"); - } + private ServiceAccount createServiceAccount(PhEeImporterRdbms resource) { + log.info("Creating ServiceAccount spec for resource: {}", resource.getMetadata().getName()); + return new ServiceAccountBuilder() + .withNewMetadata() + .withName("ph-ee-importer-rdbms") + .withNamespace(resource.getMetadata().getNamespace()) + .withOwnerReferences(createOwnerReferences(resource)) + .endMetadata() + .build(); } - private void reconcileClusterRoleBinding(PhEeImporterRdbms resource) { - ClusterRoleBinding clusterRoleBinding = createClusterRoleBinding(resource); - Resource clusterRoleBindingResource = kubernetesClient.rbac().clusterRoleBindings() - .withName("ph-ee-importer-rdbms-c-role-binding"); - if (clusterRoleBindingResource.get() == null) { - clusterRoleBindingResource.create(clusterRoleBinding); - log.info("Created new ClusterRoleBinding: {}", "ph-ee-importer-rdbms-c-role-binding"); - } - } private void reconcileRole(PhEeImporterRdbms resource) { + log.info("Reconciling Role for resource: {}", resource.getMetadata().getName()); Role role = createRole(resource); + log.info("Created Role spec: {}", role); + Resource roleResource = kubernetesClient.rbac().roles() .inNamespace(resource.getMetadata().getNamespace()) .withName("ph-ee-importer-rdbms-role"); @@ -146,145 +251,133 @@ private void reconcileRole(PhEeImporterRdbms resource) { if (roleResource.get() == null) { roleResource.create(role); log.info("Created new Role: {}", "ph-ee-importer-rdbms-role"); + } else { + roleResource.patch(role); + log.info("Updated existing Role: {}", "ph-ee-importer-rdbms-role"); } } + private Role createRole(PhEeImporterRdbms resource) { + log.info("Creating Role spec for resource: {}", resource.getMetadata().getName()); + return new RoleBuilder() + .withNewMetadata() + .withName("ph-ee-importer-rdbms-role") + .withNamespace(resource.getMetadata().getNamespace()) + .withOwnerReferences(createOwnerReferences(resource)) + .endMetadata() + .addNewRule() + .withApiGroups("") + .withResources("pods", "services", "endpoints", "persistentvolumeclaims") + .withVerbs("get", "list", "watch", "create", "update", "patch", "delete") + .endRule() + .build(); + } + + + private void reconcileRoleBinding(PhEeImporterRdbms resource) { + log.info("Reconciling RoleBinding for resource: {}", resource.getMetadata().getName()); RoleBinding roleBinding = createRoleBinding(resource); + log.info("Created RoleBinding spec: {}", roleBinding); + Resource roleBindingResource = kubernetesClient.rbac().roleBindings() .inNamespace(resource.getMetadata().getNamespace()) - .withName("ph-ee-importer-rdbms-role-binding"); + .withName("ph-ee-importer-rdbms-rolebinding"); if (roleBindingResource.get() == null) { roleBindingResource.create(roleBinding); - log.info("Created new RoleBinding: {}", "ph-ee-importer-rdbms-role-binding"); + log.info("Created new RoleBinding: {}", "ph-ee-importer-rdbms-rolebinding"); + } else { + roleBindingResource.patch(roleBinding); + log.info("Updated existing RoleBinding: {}", "ph-ee-importer-rdbms-rolebinding"); } } - - //Below are all the resource creation methods - - private Deployment createDeployment(PhEeImporterRdbms resource) { - Deployment deployment = new Deployment(); - deployment.setMetadata(resource.getMetadata()); - - DeploymentSpec deploymentSpec = new DeploymentSpec(); - deploymentSpec.setReplicas(resource.getSpec().getReplicas()); - - PodTemplateSpec podTemplateSpec = new PodTemplateSpec(); - PodSpec podSpec = new PodSpec(); - - // Set up the container specifications based on the custom resource spec - Container container = new Container(); - container.setName(resource.getMetadata().getName()); - container.setImage(resource.getSpec().getImage()); - - // Environment variables - List envVars = new ArrayList<>(); - envVars.add(new EnvVar("SPRING_PROFILES_ACTIVE", resource.getSpec().getSpringProfilesActive(), null)); - envVars.add(new EnvVar("DATASOURCE_CORE_USERNAME", resource.getSpec().getDatasource().getUsername(), null)); - envVars.add(new EnvVar("DATASOURCE_CORE_PASSWORD", null, new EnvVarSourceBuilder().withNewSecretKeyRef("database-password", "importer-rdbms-secret", false).build())); - envVars.add(new EnvVar("DATASOURCE_CORE_HOST", resource.getSpec().getDatasource().getHost(), null)); - envVars.add(new EnvVar("DATASOURCE_CORE_PORT", String.valueOf(resource.getSpec().getDatasource().getPort()), null)); - envVars.add(new EnvVar("DATASOURCE_CORE_SCHEMA", resource.getSpec().getDatasource().getSchema(), null)); - envVars.add(new EnvVar("LOGGING_LEVEL_ROOT", resource.getSpec().getLogging().getLevelRoot(), null)); - envVars.add(new EnvVar("LOGGING_PATTERN_CONSOLE", resource.getSpec().getLogging().getPatternConsole(), null)); - envVars.add(new EnvVar("JAVA_TOOL_OPTIONS", resource.getSpec().getJavaToolOptions(), null)); - envVars.add(new EnvVar("APPLICATION_BUCKET_NAME", resource.getSpec().getBucketName(), null)); - // Add AWS secrets - envVars.add(new EnvVar("CLOUD_AWS_REGION_STATIC", null, new EnvVarSourceBuilder().withNewSecretKeyRef("aws-region", "bulk-processor-secret", false).build())); - envVars.add(new EnvVar("AWS_ACCESS_KEY", null, new EnvVarSourceBuilder().withNewSecretKeyRef("aws-access-key", "bulk-processor-secret", false).build())); - envVars.add(new EnvVar("AWS_SECRET_KEY", null, new EnvVarSourceBuilder().withNewSecretKeyRef("aws-secret-key", "bulk-processor-secret", false).build())); - - container.setEnv(envVars); - - // Resource limits and requests - ResourceRequirements resourceRequirements = new ResourceRequirements(); - Map limits = new HashMap<>(); - limits.put("cpu", new Quantity(resource.getSpec().getResources().getLimits().getCpu())); - limits.put("memory", new Quantity(resource.getSpec().getResources().getLimits().getMemory())); - resourceRequirements.setLimits(limits); - - Map requests = new HashMap<>(); - requests.put("cpu", new Quantity(resource.getSpec().getResources().getRequests().getCpu())); - requests.put("memory", new Quantity(resource.getSpec().getResources().getRequests().getMemory())); - resourceRequirements.setRequests(requests); - - container.setResources(resourceRequirements); - - // Volume mounts - VolumeMount volumeMount = new VolumeMount(); - volumeMount.setName("ph-ee-config"); - volumeMount.setMountPath("/config"); - container.setVolumeMounts(Collections.singletonList(volumeMount)); - - podSpec.setContainers(Collections.singletonList(container)); - - // Volumes - Volume volume = new Volume(); - volume.setName("ph-ee-config"); - ConfigMapVolumeSource configMapVolumeSource = new ConfigMapVolumeSource(); - configMapVolumeSource.setName("ph-ee-config"); - volume.setConfigMap(configMapVolumeSource); - podSpec.setVolumes(Collections.singletonList(volume)); - - podTemplateSpec.setSpec(podSpec); - deploymentSpec.setTemplate(podTemplateSpec); - deployment.setSpec(deploymentSpec); - - return deployment; - } - - private Secret createSecret(PhEeImporterRdbms resource) { - return new SecretBuilder() + private RoleBinding createRoleBinding(PhEeImporterRdbms resource) { + log.info("Creating RoleBinding spec for resource: {}", resource.getMetadata().getName()); + return new RoleBindingBuilder() .withNewMetadata() - .withName("importer-rdbms-secret") + .withName("ph-ee-importer-rdbms-rolebinding") .withNamespace(resource.getMetadata().getNamespace()) + .withOwnerReferences(createOwnerReferences(resource)) .endMetadata() - .addToData("database-password", Base64.getEncoder().encodeToString(resource.getSpec().getDatasource().getPassword().getBytes())) + .withSubjects(new SubjectBuilder() + .withKind("ServiceAccount") + .withName("ph-ee-importer-rdbms") + .withNamespace(resource.getMetadata().getNamespace()) + .build()) + .withRoleRef(new RoleRefBuilder() + .withApiGroup("rbac.authorization.k8s.io") + .withKind("Role") + .withName("ph-ee-importer-rdbms-role") + .build()) .build(); } - private ConfigMap createConfigMap(PhEeImporterRdbms resource) { - return new ConfigMapBuilder() - .withNewMetadata() - .withName("ph-ee-config") - .withNamespace(resource.getMetadata().getNamespace()) - .endMetadata() - .addToData("config-file-name", "config-file-content") // Add actual config data - .build(); - } - private ServiceAccount createServiceAccount(PhEeImporterRdbms resource) { - return new ServiceAccountBuilder() - .withNewMetadata() - .withName("ph-ee-importer-rdbms") - .withNamespace(resource.getMetadata().getNamespace()) - .addToLabels("app", "ph-ee-importer-rdbms") - .addToLabels("chart", resource.getMetadata().getLabels().get("chart")) - .addToLabels("heritage", resource.getMetadata().getLabels().get("heritage")) - .addToLabels("release", resource.getMetadata().getLabels().get("release")) - .endMetadata() - .build(); + + private void reconcileClusterRole(PhEeImporterRdbms resource) { + log.info("Reconciling ClusterRole for resource: {}", resource.getMetadata().getName()); + ClusterRole clusterRole = createClusterRole(resource); + log.info("Created ClusterRole spec: {}", clusterRole); + + Resource clusterRoleResource = kubernetesClient.rbac().clusterRoles() + .withName("ph-ee-importer-rdbms-clusterrole"); + + if (clusterRoleResource.get() == null) { + clusterRoleResource.create(clusterRole); + log.info("Created new ClusterRole: {}", "ph-ee-importer-rdbms-clusterrole"); + } else { + clusterRoleResource.patch(clusterRole); + log.info("Updated existing ClusterRole: {}", "ph-ee-importer-rdbms-clusterrole"); + } } private ClusterRole createClusterRole(PhEeImporterRdbms resource) { + log.info("Creating ClusterRole spec for resource: {}", resource.getMetadata().getName()); return new ClusterRoleBuilder() .withNewMetadata() - .withName("ph-ee-importer-rdbms-c-role") + .withName("ph-ee-importer-rdbms-clusterrole") + .withOwnerReferences(createOwnerReferences(resource)) .endMetadata() .addNewRule() .withApiGroups("") - .withResources("secrets") - .withVerbs("get", "watch", "list") + .withResources("pods", "services", "endpoints", "persistentvolumeclaims") + .withVerbs("get", "list", "watch", "create", "update", "patch", "delete") + .endRule() + .addNewRule() + .withApiGroups("apps") + .withResources("deployments") + .withVerbs("get", "list", "watch", "create", "update", "patch", "delete") .endRule() .build(); } + + + private void reconcileClusterRoleBinding(PhEeImporterRdbms resource) { + log.info("Reconciling ClusterRoleBinding for resource: {}", resource.getMetadata().getName()); + ClusterRoleBinding clusterRoleBinding = createClusterRoleBinding(resource); + log.info("Created ClusterRoleBinding spec: {}", clusterRoleBinding); + + Resource clusterRoleBindingResource = kubernetesClient.rbac().clusterRoleBindings() + .withName("ph-ee-importer-rdbms-clusterrolebinding"); + + if (clusterRoleBindingResource.get() == null) { + clusterRoleBindingResource.create(clusterRoleBinding); + log.info("Created new ClusterRoleBinding: {}", "ph-ee-importer-rdbms-clusterrolebinding"); + } else { + clusterRoleBindingResource.patch(clusterRoleBinding); + log.info("Updated existing ClusterRoleBinding: {}", "ph-ee-importer-rdbms-clusterrolebinding"); + } + } + private ClusterRoleBinding createClusterRoleBinding(PhEeImporterRdbms resource) { + log.info("Creating ClusterRoleBinding spec for resource: {}", resource.getMetadata().getName()); return new ClusterRoleBindingBuilder() .withNewMetadata() - .withName("ph-ee-importer-rdbms-c-role-binding") + .withName("ph-ee-importer-rdbms-clusterrolebinding") + .withOwnerReferences(createOwnerReferences(resource)) .endMetadata() .withSubjects(new SubjectBuilder() .withKind("ServiceAccount") @@ -292,43 +385,25 @@ private ClusterRoleBinding createClusterRoleBinding(PhEeImporterRdbms resource) .withNamespace(resource.getMetadata().getNamespace()) .build()) .withRoleRef(new RoleRefBuilder() - .withKind("ClusterRole") - .withName("ph-ee-importer-rdbms-c-role") .withApiGroup("rbac.authorization.k8s.io") + .withKind("ClusterRole") + .withName("ph-ee-importer-rdbms-clusterrole") .build()) .build(); } - private Role createRole(PhEeImporterRdbms resource) { - return new RoleBuilder() - .withNewMetadata() - .withName("ph-ee-importer-rdbms-role") - .withNamespace(resource.getMetadata().getNamespace()) - .endMetadata() - .addNewRule() - .withApiGroups("") - .withResources("pods", "configmaps") - .withVerbs("get", "create", "update") - .endRule() - .build(); - } - private RoleBinding createRoleBinding(PhEeImporterRdbms resource) { - return new RoleBindingBuilder() - .withNewMetadata() - .withName("ph-ee-importer-rdbms-role-binding") - .withNamespace(resource.getMetadata().getNamespace()) - .endMetadata() - .withSubjects(new SubjectBuilder() - .withKind("ServiceAccount") - .withName("ph-ee-importer-rdbms") - .withNamespace(resource.getMetadata().getNamespace()) - .build()) - .withRoleRef(new RoleRefBuilder() - .withKind("Role") - .withName("ph-ee-importer-rdbms-role") - .withApiGroup("rbac.authorization.k8s.io") - .build()) - .build(); + + private List createOwnerReferences(PhEeImporterRdbms resource) { + return Collections.singletonList( + new OwnerReferenceBuilder() + .withApiVersion(resource.getApiVersion()) + .withKind(resource.getKind()) + .withName(resource.getMetadata().getName()) + .withUid(resource.getMetadata().getUid()) + .withController(true) + .withBlockOwnerDeletion(true) + .build() + ); } -} +} \ No newline at end of file diff --git a/PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsSpec.java b/PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsSpec.java deleted file mode 100644 index 781ddda..0000000 --- a/PHEE-operator/src/main/java/com/example/PhEeImporterRdbmsSpec.java +++ /dev/null @@ -1,216 +0,0 @@ -package com.example.operator; - -public class PhEeImporterRdbmsSpec { - - private String image; - private int replicas; - private String springProfilesActive; - private Datasource datasource; - private Resources resources; - private Logging logging; - private String javaToolOptions; - private String bucketName; - - // Getters and Setters - - public String getImage() { - return image; - } - - public void setImage(String image) { - this.image = image; - } - - public int getReplicas() { - return replicas; - } - - public void setReplicas(int replicas) { - this.replicas = replicas; - } - - public String getSpringProfilesActive() { - return springProfilesActive; - } - - public void setSpringProfilesActive(String springProfilesActive) { - this.springProfilesActive = springProfilesActive; - } - - public Datasource getDatasource() { - return datasource; - } - - public void setDatasource(Datasource datasource) { - this.datasource = datasource; - } - - public Resources getResources() { - return resources; - } - - public void setResources(Resources resources) { - this.resources = resources; - } - - public Logging getLogging() { - return logging; - } - - public void setLogging(Logging logging) { - this.logging = logging; - } - - public String getJavaToolOptions() { - return javaToolOptions; - } - - public void setJavaToolOptions(String javaToolOptions) { - this.javaToolOptions = javaToolOptions; - } - - public String getBucketName() { - return bucketName; - } - - public void setBucketName(String bucketName) { - this.bucketName = bucketName; - } - - public static class Datasource { - private String username; - private String password; - private String host; - private int port; - private String schema; - - // Getters and Setters - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - } - - public static class Resources { - private Limits limits; - private Requests requests; - - // Getters and Setters - public Limits getLimits() { - return limits; - } - - public void setLimits(Limits limits) { - this.limits = limits; - } - - public Requests getRequests() { - return requests; - } - - public void setRequests(Requests requests) { - this.requests = requests; - } - - public static class Limits { - private String cpu; - private String memory; - - // Getters and Setters - public String getCpu() { - return cpu; - } - - public void setCpu(String cpu) { - this.cpu = cpu; - } - - public String getMemory() { - return memory; - } - - public void setMemory(String memory) { - this.memory = memory; - } - } - - public static class Requests { - private String cpu; - private String memory; - - // Getters and Setters - public String getCpu() { - return cpu; - } - - public void setCpu(String cpu) { - this.cpu = cpu; - } - - public String getMemory() { - return memory; - } - - public void setMemory(String memory) { - this.memory = memory; - } - } - } - - public static class Logging { - private String levelRoot; - private String patternConsole; - - // Getters and Setters - public String getLevelRoot() { - return levelRoot; - } - - public void setLevelRoot(String levelRoot) { - this.levelRoot = levelRoot; - } - - public String getPatternConsole() { - return patternConsole; - } - - public void setPatternConsole(String patternConsole) { - this.patternConsole = patternConsole; - } - } -} diff --git a/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbms.java b/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbms.java new file mode 100644 index 0000000..a5c8ed8 --- /dev/null +++ b/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbms.java @@ -0,0 +1,15 @@ +package com.example.customresource; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; +import com.example.customresource.PhEeImporterRdbmsSpec; +import com.example.customresource.PhEeImporterRdbmsStatus; +import io.fabric8.kubernetes.model.annotation.Plural; + +@Version("v1") +@Group("my.custom.group") +@Plural("pheeimporterrdbmses") +public class PhEeImporterRdbms extends CustomResource implements Namespaced { +} diff --git a/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsSpec.java b/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsSpec.java new file mode 100644 index 0000000..c7a0053 --- /dev/null +++ b/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsSpec.java @@ -0,0 +1,372 @@ +package com.example.customresource; + +import java.util.Objects; + +public class PhEeImporterRdbmsSpec { + private Integer replicas; + private String image; + private String springProfilesActive; + private Datasource datasource; + private Resources resources; + private Logging logging; + private String javaToolOptions; + private String bucketName; + + public PhEeImporterRdbmsSpec() { + } + + public PhEeImporterRdbmsSpec(Integer replicas, String image, String springProfilesActive, + Datasource datasource, Resources resources, Logging logging, + String javaToolOptions, String bucketName) { + this.replicas = replicas; + this.image = image; + this.springProfilesActive = springProfilesActive; + this.datasource = datasource; + this.resources = resources; + this.logging = logging; + this.javaToolOptions = javaToolOptions; + this.bucketName = bucketName; + } + + public Integer getReplicas() { + return replicas; + } + + public void setReplicas(Integer replicas) { + this.replicas = replicas; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public String getSpringProfilesActive() { + return springProfilesActive; + } + + public void setSpringProfilesActive(String springProfilesActive) { + this.springProfilesActive = springProfilesActive; + } + + public Datasource getDatasource() { + return datasource; + } + + public void setDatasource(Datasource datasource) { + this.datasource = datasource; + } + + public Resources getResources() { + return resources; + } + + public void setResources(Resources resources) { + this.resources = resources; + } + + public Logging getLogging() { + return logging; + } + + public void setLogging(Logging logging) { + this.logging = logging; + } + + public String getJavaToolOptions() { + return javaToolOptions; + } + + public void setJavaToolOptions(String javaToolOptions) { + this.javaToolOptions = javaToolOptions; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + @Override + public String toString() { + return "PhEeImporterRdbmsSpec{" + + "replicas=" + replicas + + ", image='" + image + '\'' + + ", springProfilesActive='" + springProfilesActive + '\'' + + ", datasource=" + datasource + + ", resources=" + resources + + ", logging=" + logging + + ", javaToolOptions='" + javaToolOptions + '\'' + + ", bucketName='" + bucketName + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PhEeImporterRdbmsSpec)) return false; + PhEeImporterRdbmsSpec that = (PhEeImporterRdbmsSpec) o; + return Objects.equals(getReplicas(), that.getReplicas()) && + Objects.equals(getImage(), that.getImage()) && + Objects.equals(getSpringProfilesActive(), that.getSpringProfilesActive()) && + Objects.equals(getDatasource(), that.getDatasource()) && + Objects.equals(getResources(), that.getResources()) && + Objects.equals(getLogging(), that.getLogging()) && + Objects.equals(getJavaToolOptions(), that.getJavaToolOptions()) && + Objects.equals(getBucketName(), that.getBucketName()); + } + + @Override + public int hashCode() { + return Objects.hash(getReplicas(), getImage(), getSpringProfilesActive(), + getDatasource(), getResources(), getLogging(), + getJavaToolOptions(), getBucketName()); + } + + // Inner classes for nested objects + + public static class Datasource { + private String username; + private String password; + private String host; + private Integer port; + private String schema; + + public Datasource() { + } + + public Datasource(String username, String password, String host, Integer port, String schema) { + this.username = username; + this.password = password; + this.host = host; + this.port = port; + this.schema = schema; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + @Override + public String toString() { + return "Datasource{" + + "username='" + username + '\'' + + ", password='[PROTECTED]'" + + ", host='" + host + '\'' + + ", port=" + port + + ", schema='" + schema + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Datasource)) return false; + Datasource that = (Datasource) o; + return Objects.equals(getUsername(), that.getUsername()) && + Objects.equals(getPassword(), that.getPassword()) && + Objects.equals(getHost(), that.getHost()) && + Objects.equals(getPort(), that.getPort()) && + Objects.equals(getSchema(), that.getSchema()); + } + + @Override + public int hashCode() { + return Objects.hash(getUsername(), getPassword(), getHost(), getPort(), getSchema()); + } + } + + public static class Resources { + private ResourceDetails limits; + private ResourceDetails requests; + + public Resources() { + } + + public Resources(ResourceDetails limits, ResourceDetails requests) { + this.limits = limits; + this.requests = requests; + } + + public ResourceDetails getLimits() { + return limits; + } + + public void setLimits(ResourceDetails limits) { + this.limits = limits; + } + + public ResourceDetails getRequests() { + return requests; + } + + public void setRequests(ResourceDetails requests) { + this.requests = requests; + } + + @Override + public String toString() { + return "Resources{" + + "limits=" + limits + + ", requests=" + requests + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Resources)) return false; + Resources that = (Resources) o; + return Objects.equals(getLimits(), that.getLimits()) && + Objects.equals(getRequests(), that.getRequests()); + } + + @Override + public int hashCode() { + return Objects.hash(getLimits(), getRequests()); + } + } + + public static class ResourceDetails { + private String cpu; + private String memory; + + public ResourceDetails() { + } + + public ResourceDetails(String cpu, String memory) { + this.cpu = cpu; + this.memory = memory; + } + + public String getCpu() { + return cpu; + } + + public void setCpu(String cpu) { + this.cpu = cpu; + } + + public String getMemory() { + return memory; + } + + public void setMemory(String memory) { + this.memory = memory; + } + + @Override + public String toString() { + return "ResourceDetails{" + + "cpu='" + cpu + '\'' + + ", memory='" + memory + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ResourceDetails)) return false; + ResourceDetails that = (ResourceDetails) o; + return Objects.equals(getCpu(), that.getCpu()) && + Objects.equals(getMemory(), that.getMemory()); + } + + @Override + public int hashCode() { + return Objects.hash(getCpu(), getMemory()); + } + } + + public static class Logging { + private String levelRoot; + private String patternConsole; + + public Logging() { + } + + public Logging(String levelRoot, String patternConsole) { + this.levelRoot = levelRoot; + this.patternConsole = patternConsole; + } + + public String getLevelRoot() { + return levelRoot; + } + + public void setLevelRoot(String levelRoot) { + this.levelRoot = levelRoot; + } + + public String getPatternConsole() { + return patternConsole; + } + + public void setPatternConsole(String patternConsole) { + this.patternConsole = patternConsole; + } + + @Override + public String toString() { + return "Logging{" + + "levelRoot='" + levelRoot + '\'' + + ", patternConsole='" + patternConsole + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Logging)) return false; + Logging that = (Logging) o; + return Objects.equals(getLevelRoot(), that.getLevelRoot()) && + Objects.equals(getPatternConsole(), that.getPatternConsole()); + } + + @Override + public int hashCode() { + return Objects.hash(getLevelRoot(), getPatternConsole()); + } + } +} diff --git a/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsStatus.java b/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsStatus.java new file mode 100644 index 0000000..c5cbd20 --- /dev/null +++ b/PHEE-operator/src/main/java/com/example/customresource/PhEeImporterRdbmsStatus.java @@ -0,0 +1,78 @@ +package com.example.customresource; + +import java.util.Objects; + +public class PhEeImporterRdbmsStatus { + private Integer availableReplicas; + private String errorMessage; + private String lastAppliedImage; + private boolean ready; + + public PhEeImporterRdbmsStatus() { + } + + public PhEeImporterRdbmsStatus(Integer availableReplicas, String errorMessage, String lastAppliedImage, boolean ready) { + this.availableReplicas = availableReplicas; + this.errorMessage = errorMessage; + this.lastAppliedImage = lastAppliedImage; + this.ready = ready; + } + + public Integer getAvailableReplicas() { + return availableReplicas; + } + + public void setAvailableReplicas(Integer availableReplicas) { + this.availableReplicas = availableReplicas; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public String getLastAppliedImage() { + return lastAppliedImage; + } + + public void setLastAppliedImage(String lastAppliedImage) { + this.lastAppliedImage = lastAppliedImage; + } + + public boolean isReady() { + return ready; + } + + public void setReady(boolean ready) { + this.ready = ready; + } + + @Override + public String toString() { + return "PhEeImporterRdbmsStatus{" + + "availableReplicas=" + availableReplicas + + ", errorMessage='" + errorMessage + '\'' + + ", lastAppliedImage='" + lastAppliedImage + '\'' + + ", ready=" + ready + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PhEeImporterRdbmsStatus)) return false; + PhEeImporterRdbmsStatus that = (PhEeImporterRdbmsStatus) o; + return ready == that.ready && + Objects.equals(availableReplicas, that.availableReplicas) && + Objects.equals(errorMessage, that.errorMessage) && + Objects.equals(lastAppliedImage, that.lastAppliedImage); + } + + @Override + public int hashCode() { + return Objects.hash(availableReplicas, errorMessage, lastAppliedImage, ready); + } +} diff --git a/PHEE-operator/src/main/java/com/example/utils/LoggingUtil.java b/PHEE-operator/src/main/java/com/example/utils/LoggingUtil.java new file mode 100644 index 0000000..8e83dc4 --- /dev/null +++ b/PHEE-operator/src/main/java/com/example/utils/LoggingUtil.java @@ -0,0 +1,66 @@ +package com.example.utils; + +import com.example.customresource.PhEeImporterRdbms; +import com.example.customresource.PhEeImporterRdbmsSpec; +import com.example.customresource.PhEeImporterRdbmsSpec.Datasource; +import com.example.customresource.PhEeImporterRdbmsSpec.Resources; +import com.example.customresource.PhEeImporterRdbmsSpec.Logging; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoggingUtil { + + private static final Logger log = LoggerFactory.getLogger(LoggingUtil.class); + + public static void logResourceDetails(PhEeImporterRdbms resource) { + PhEeImporterRdbmsSpec spec = resource.getSpec(); + Integer replicas = spec.getReplicas(); + String image = spec.getImage(); + String springProfilesActive = spec.getSpringProfilesActive(); + Datasource datasource = spec.getDatasource(); + Resources resources = spec.getResources(); + Logging loggingConfig = spec.getLogging(); + String javaToolOptions = spec.getJavaToolOptions(); + String bucketName = spec.getBucketName(); + + log.info("Reconciling PhEeImporterRdbms: {}", resource.getMetadata().getName()); + log.info("Desired state - Replicas: {}, Image: {}, Spring Profiles Active: {}, Java Tool Options: {}, Bucket Name: {}", + replicas, image, springProfilesActive, javaToolOptions, bucketName); + + if (datasource != null) { + log.info("Datasource Config - Username: {}, Host: {}, Port: {}, Schema: {}", + datasource.getUsername(), datasource.getHost(), datasource.getPort(), datasource.getSchema()); + } else { + log.warn("No Datasource Config specified in the Spec."); + } + + if (resources != null) { + if (resources.getLimits() != null) { + log.info("Resource Limits - CPU: {}, Memory: {}", + resources.getLimits().getCpu(), resources.getLimits().getMemory()); + } else { + log.warn("No Resource Limits specified."); + } + + if (resources.getRequests() != null) { + log.info("Resource Requests - CPU: {}, Memory: {}", + resources.getRequests().getCpu(), resources.getRequests().getMemory()); + } else { + log.warn("No Resource Requests specified."); + } + } else { + log.warn("No Resource Config specified in the Spec."); + } + + if (loggingConfig != null) { + log.info("Logging Config - Level Root: {}, Pattern Console: {}", + loggingConfig.getLevelRoot(), loggingConfig.getPatternConsole()); + } else { + log.warn("No Logging Config specified in the Spec."); + } + } + + public static void logError(String message, Exception e) { + log.error(message, e); + } +} diff --git a/PHEE-operator/src/main/java/com/example/utils/StatusUpdateUtil.java b/PHEE-operator/src/main/java/com/example/utils/StatusUpdateUtil.java new file mode 100644 index 0000000..0076afc --- /dev/null +++ b/PHEE-operator/src/main/java/com/example/utils/StatusUpdateUtil.java @@ -0,0 +1,39 @@ +package com.example.utils; + +import com.example.customresource.PhEeImporterRdbms; +import com.example.customresource.PhEeImporterRdbmsStatus; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StatusUpdateUtil { + + private static final Logger log = LoggerFactory.getLogger(StatusUpdateUtil.class); + + public static UpdateControl updateStatus(KubernetesClient kubernetesClient, PhEeImporterRdbms resource, Integer replicas, String image, boolean isReady, String errorMessage) { + PhEeImporterRdbmsStatus status = new PhEeImporterRdbmsStatus(); + status.setAvailableReplicas(replicas); + status.setLastAppliedImage(image); + status.setReady(isReady); + status.setErrorMessage(errorMessage); + + resource.setStatus(status); + log.info("Updating Status - Available Replicas: {}, Last Applied Image: {}, Ready: {}, Error Message: {}", + status.getAvailableReplicas(), status.getLastAppliedImage(), status.isReady(), status.getErrorMessage()); + + if (kubernetesClient.resources(PhEeImporterRdbms.class) + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getMetadata().getName()) + .get() != null) { + return UpdateControl.patchStatus(resource); + } else { + log.error("Resource not found for status update: {}", resource.getMetadata().getName()); + return UpdateControl.noUpdate(); + } + } + + public static UpdateControl updateErrorStatus(KubernetesClient kubernetesClient, PhEeImporterRdbms resource, String image, Exception e) { + return updateStatus(kubernetesClient, resource, 0, image, false, "Error during reconciliation: " + e.getMessage()); + } +}